Fixes on bills and implemented domains serializer create() and update()

This commit is contained in:
Marc Aymerich 2015-05-28 09:43:57 +00:00
parent fd8d805b5e
commit 1223ed85e6
13 changed files with 692 additions and 147 deletions

View file

@ -412,5 +412,4 @@ touch /tmp/somefile
# batch zone edditing
# inherit registers from parent?
# Bill metric disk 5 GB: unialber: include not include 5, unialbert recheck period
# Disable pagination on membership fees (allways one page)

View file

@ -32,7 +32,7 @@ def download_bills(modeladmin, request, queryset):
response['Content-Disposition'] = 'attachment; filename="orchestra-bills.zip"'
return response
bill = queryset.get()
pdf = html_to_pdf(bill.html or bill.render())
pdf = html_to_pdf(bill.html or bill.render(), pagination=bill.has_multiple_pages)
return HttpResponse(pdf, content_type='application/pdf')
download_bills.verbose_name = _("Download")
download_bills.url_name = 'download'
@ -78,9 +78,13 @@ def close_bills(modeladmin, request, queryset):
else:
url = reverse('admin:transactions_transaction_changelist')
url += 'id__in=%s' % ','.join(map(str, transactions))
context = {
'url': url,
'num': num,
}
message = ungettext(
_('<a href="%s">One related transaction</a> has been created') % url,
_('<a href="%s">%i related transactions</a> have been created') % (url, num),
_('<a href="%(url)s">One related transaction</a> has been created') % context,
_('<a href="%(url)s">%(num)i related transactions</a> have been created') % context,
num)
messages.success(request, mark_safe(message))
return
@ -105,12 +109,22 @@ close_bills.url_name = 'close'
def send_bills(modeladmin, request, queryset):
num = 0
for bill in queryset:
if not validate_contact(request, bill):
return
for bill in queryset:
num += 1
if num == 1:
bill.send()
else:
# Batch email
queryset.send()
for bill in queryset:
modeladmin.log_change(request, bill, 'Sent')
messages.success(request, ungetetx(
_("One bill has been sent."),
_("%i bills have been sent.") % num,
num))
send_bills.verbose_name = lambda bill: _("Resend" if getattr(bill, 'is_sent', False) else "Send")
send_bills.url_name = 'send'
@ -153,7 +167,7 @@ def undo_billing(modeladmin, request, queryset):
else:
# First iteration
if order.billed_on < line.start_on:
messages.error(request, "billed on is smaller than first line start_on.")
messages.error(request, "Billed on is smaller than first line start_on.")
return
prev = line.end_on
nlines += 1
@ -168,6 +182,7 @@ def undo_billing(modeladmin, request, queryset):
for line in lines:
nlines += 1
line.delete()
# TODO update order history undo billing
order.save(update_fields=('billed_until', 'billed_on'))
norders += 1
@ -177,29 +192,14 @@ def undo_billing(modeladmin, request, queryset):
})
# TODO son't check for account equality
def move_lines(modeladmin, request, queryset):
def move_lines(modeladmin, request, queryset, action=None):
# Validate
account = None
for line in queryset.select_related('bill'):
bill = line.bill
if not bill.is_open:
messages.error(request, _("Can not move lines from a closed bill."))
return
elif not account:
account = bill.account
elif bill.account != account:
messages.error(request, _("Can not move lines from different accounts"))
return
target = request.GET.get('target')
if not target:
# select target
context = {}
return render(request, 'admin/orchestra/generic_confirmation.html', context)
target = Bill.objects.get(pk=int(pk))
if target.account != account:
messages.error(request, _("Target account different than lines account."))
return
if request.POST.get('post') == 'generic_confirmation':
for line in queryset:
line.bill = target
@ -212,9 +212,4 @@ def move_lines(modeladmin, request, queryset):
def copy_lines(modeladmin, request, queryset):
# same as move, but changing action behaviour
pass
def delete_lines(modeladmin, request, queryset):
# Call contrib.admin delete action if all lines in open bill
pass
return move_lines(modeladmin, request, queryset)

View file

@ -1,6 +1,6 @@
from django import forms
from django.conf.urls import url
from django.contrib import admin
from django.contrib import admin, messages
from django.contrib.admin.utils import unquote
from django.core.urlresolvers import reverse
from django.db import models
@ -9,6 +9,7 @@ from django.db.models.functions import Coalesce
from django.templatetags.static import static
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from django.shortcuts import redirect
from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import admin_date, insertattr, admin_link
@ -101,16 +102,22 @@ class ClosedBillLineInline(BillLineInline):
class BillLineAdmin(admin.ModelAdmin):
list_display = (
'description', 'bill_link', 'rate', 'quantity', 'tax', 'subtotal', 'display_sublinetotal',
'display_total'
'description', 'bill_link', 'display_is_open', 'account_link', 'rate', 'quantity', 'tax',
'subtotal', 'display_sublinetotal', 'display_total'
)
actions = (actions.undo_billing, actions.move_lines, actions.copy_lines,)
list_filter = ('tax', ('bill', admin.RelatedOnlyFieldListFilter))
list_filter = ('tax', ('bill', admin.RelatedOnlyFieldListFilter), 'bill__is_open')
list_select_related = ('bill',)
search_fields = ('description', 'bill__number')
account_link = admin_link('bill__account')
bill_link = admin_link('bill')
def display_is_open(self, instance):
return instance.bill.is_open
display_is_open.short_description = _("Is open")
display_is_open.boolean = True
def display_sublinetotal(self, instance):
return instance.subline_total or ''
display_sublinetotal.short_description = _("Subline")
@ -140,18 +147,26 @@ class BillLineManagerAdmin(BillLineAdmin):
return qset
def changelist_view(self, request, extra_context=None):
GET = request.GET.copy()
bill_ids = GET.pop('ids', None)
GET_copy = request.GET.copy()
bill_ids = GET_copy.pop('ids', None)
if bill_ids:
request.GET = GET
bill_ids = list(map(int, bill_ids))
bill_ids = bill_ids[0]
request.GET = GET_copy
bill_ids = list(map(int, bill_ids.split(',')))
else:
messages.error(request, _("No bills selected."))
return redirect('..')
self.bill_ids = bill_ids
if bill_ids and len(bill_ids) == 1:
if len(bill_ids) == 1:
bill_url = reverse('admin:bills_bill_change', args=(bill_ids[0],))
bill = Bill.objects.get(pk=bill_ids[0])
bill_link = '<a href="%s">%s</a>' % (bill_url, bill.number)
title = mark_safe(_("Manage %s bill lines.") % bill_link)
if not bill.is_open:
messages.warning(request, _("Bill not in open state."))
else:
if Bill.objects.filter(id__in=bill_ids, is_open=False).exists():
messages.warning(request, _("Not all bills are in open state."))
title = _("Manage bill lines of multiple bills.")
context = {
'title': title,
@ -177,13 +192,15 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
'fields': ('html',),
}),
)
list_prefetch_related = ('transactions',)
search_fields = ('number', 'account__username', 'comments')
change_view_actions = [
actions.manage_lines, actions.view_bill, actions.download_bills, actions.send_bills,
actions.close_bills
]
list_prefetch_related = ('transactions',)
search_fields = ('number', 'account__username', 'comments')
actions = [actions.download_bills, actions.close_bills, actions.send_bills]
actions = [
actions.manage_lines, actions.download_bills, actions.close_bills, actions.send_bills
]
change_readonly_fields = ('account_link', 'type', 'is_open')
readonly_fields = ('number', 'display_total', 'is_sent', 'display_payment_state')
inlines = [BillLineInline, ClosedBillLineInline]

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-05-27 13:28+0000\n"
"POT-Creation-Date: 2015-05-28 09:23+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -34,97 +34,114 @@ msgstr ""
msgid "Selected bills have been closed"
msgstr ""
#: actions.py:82
#: actions.py:86
#, python-format
msgid "<a href=\"%s\">One related transaction</a> has been created"
msgid "<a href=\"%(url)s\">One related transaction</a> has been created"
msgstr ""
#: actions.py:83
#: actions.py:87
#, python-format
msgid "<a href=\"%s\">%i related transactions</a> have been created"
msgid "<a href=\"%(url)s\">%(num)i related transactions</a> have been created"
msgstr ""
#: actions.py:89
#: actions.py:93
msgid "Are you sure about closing the following bills?"
msgstr ""
#: actions.py:90
#: actions.py:94
msgid ""
"Once a bill is closed it can not be further modified.</p><p>Please select a "
"payment source for the selected bills"
msgstr ""
#: actions.py:103
#: actions.py:107
msgid "Close"
msgstr ""
#: actions.py:114
#: actions.py:125
msgid "One bill has been sent."
msgstr ""
#: actions.py:126
#, python-format
msgid "%i bills have been sent."
msgstr ""
#: actions.py:128
msgid "Resend"
msgstr ""
#: actions.py:174
#: actions.py:189
#, python-format
msgid "%(norders)s orders and %(nlines)s lines undoed."
msgstr ""
#: actions.py:187
msgid "Can not move lines from a closed bill."
msgstr ""
#: actions.py:192
msgid "Can not move lines from different accounts"
msgstr ""
#: actions.py:201
msgid "Target account different than lines account."
msgstr ""
#: actions.py:208
msgid "Lines moved"
msgstr ""
#: admin.py:48 admin.py:92 admin.py:121 forms.py:11
#: admin.py:49 admin.py:93 admin.py:128 forms.py:11
msgid "Total"
msgstr ""
#: admin.py:79
#: admin.py:80
msgid "Description"
msgstr "Descripció"
#: admin.py:87
#: admin.py:88
msgid "Subtotal"
msgstr ""
#: admin.py:116
#: admin.py:118
msgid "Is open"
msgstr ""
#: admin.py:123
msgid "Subline"
msgstr ""
#: admin.py:153
#: admin.py:157
msgid "No bills selected."
msgstr ""
#: admin.py:164
#, python-format
msgid "Manage %s bill lines."
msgstr ""
#: admin.py:155
#: admin.py:166
msgid "Bill not in open state."
msgstr ""
#: admin.py:169
msgid "Not all bills are in open state."
msgstr ""
#: admin.py:170
msgid "Manage bill lines of multiple bills."
msgstr ""
#: admin.py:175
#: admin.py:190
msgid "Raw"
msgstr ""
#: admin.py:196
#: admin.py:208
msgid "Created"
msgstr ""
#: admin.py:213
msgid "lines"
msgstr ""
#: admin.py:201 templates/bills/microspective.html:118
#: admin.py:218 templates/bills/microspective.html:118
msgid "total"
msgstr ""
#: admin.py:209 models.py:88 models.py:340
#: admin.py:226 models.py:88 models.py:352
msgid "type"
msgstr "tipus"
#: admin.py:226
#: admin.py:243
msgid "Payment"
msgstr "Pagament"
@ -290,82 +307,78 @@ msgstr "comentaris"
msgid "HTML"
msgstr ""
#: models.py:280
#: models.py:285
msgid "bill"
msgstr ""
#: models.py:281 models.py:338 templates/bills/microspective.html:73
#: models.py:286 models.py:350 templates/bills/microspective.html:73
msgid "description"
msgstr "descripció"
#: models.py:282
#: models.py:287
msgid "rate"
msgstr "tarifa"
#: models.py:283
#: models.py:288
msgid "quantity"
msgstr "quantitat"
#: models.py:284
#: models.py:289
#, fuzzy
#| msgid "quantity"
msgid "Verbose quantity"
msgstr "quantitat"
#: models.py:285 templates/bills/microspective.html:77
#: models.py:290 templates/bills/microspective.html:77
#: templates/bills/microspective.html:111
msgid "subtotal"
msgstr ""
#: models.py:286
#: models.py:291
msgid "tax"
msgstr "impostos"
#: models.py:287
#: models.py:292
msgid "start"
msgstr ""
#: models.py:288
#: models.py:293
msgid "end"
msgstr ""
#: models.py:290
#: models.py:295
msgid "Informative link back to the order"
msgstr ""
#: models.py:291
#: models.py:296
msgid "order billed"
msgstr ""
#: models.py:292
#: models.py:297
msgid "order billed until"
msgstr ""
#: models.py:293
#: models.py:298
msgid "created"
msgstr ""
#: models.py:295
#: models.py:300
msgid "amended line"
msgstr ""
#: models.py:316
msgid "{ini} to {end}"
msgstr ""
#: models.py:331
#: models.py:343
msgid "Volume"
msgstr ""
#: models.py:332
#: models.py:344
msgid "Compensation"
msgstr ""
#: models.py:333
#: models.py:345
msgid "Other"
msgstr ""
#: models.py:337
#: models.py:349
msgid "bill line"
msgstr ""
@ -378,20 +391,15 @@ msgstr ""
msgid "On %(bank_account)s"
msgstr ""
#: templates/bills/microspective-fee.html:113
#: templates/bills/microspective-fee.html:114
#, python-format
msgid "From %(period)s"
msgid "From %(ini)s to %(end)s"
msgstr ""
#: templates/bills/microspective-fee.html:118
#: templates/bills/microspective-fee.html:121
msgid ""
"\n"
"<strong>Con vuestras cuotas</strong>, ademas de obtener conexion y multitud "
"de servicios, estais<br>\n"
" Con vuestras cuotas, ademas de obtener conexion y multitud de servicios,"
"<br>\n"
" Con vuestras cuotas, ademas de obtener conexion <br>\n"
" Con vuestras cuotas, ademas de obtener <br>\n"
"<strong>With your membership</strong> you are supporting ...\n"
msgstr ""
#: templates/bills/microspective.html:49
@ -403,8 +411,9 @@ msgid "TOTAL"
msgstr "TOTAL"
#: templates/bills/microspective.html:57
#, python-format
msgid "%(bill_type|upper)s DATE "
#, fuzzy, python-format
#| msgid "%(bill_type|upper)s DATE "
msgid "%(bill_type)s DATE"
msgstr "DATA %(bill_type|upper)s"
#: templates/bills/microspective.html:74
@ -437,10 +446,18 @@ msgid "PAYMENT"
msgstr "PAGAMENT"
#: templates/bills/microspective.html:140
#, python-format
#, fuzzy, python-format
#| msgid ""
#| "\n"
#| " You can pay our <i>%(type)s</i> by bank transfer. "
#| "<br>\n"
#| " Please make sure to state your name and the <i>"
#| "%(type)s</i> number.\n"
#| " Our bank account number is <br>\n"
#| " "
msgid ""
"\n"
" You can pay our <i>%(type)s</i> by bank transfer. <br>\n"
" You can pay our <i>%(type)s</i> by bank transfer.<br>\n"
" Please make sure to state your name and the <i>%(type)s</"
"i> number.\n"
" Our bank account number is <br>\n"
@ -459,8 +476,8 @@ msgstr "PREGUNTES"
msgid ""
"\n"
" If you have any question about your <i>%(type)s</i>, please\n"
" feel free to contact us at your convinience. We will reply as "
"soon as we get\n"
" feel free to write us at %(email)s. We will reply as soon as we "
"get\n"
" your message.\n"
" "
msgstr ""

View file

@ -0,0 +1,469 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-05-28 09:23+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: actions.py:37
msgid "Download"
msgstr ""
#: actions.py:47
msgid "View"
msgstr ""
#: actions.py:55
msgid "Selected bills should be in open state"
msgstr ""
#: actions.py:73
msgid "Selected bills have been closed"
msgstr ""
#: actions.py:86
#, python-format
msgid "<a href=\"%(url)s\">One related transaction</a> has been created"
msgstr ""
#: actions.py:87
#, python-format
msgid "<a href=\"%(url)s\">%(num)i related transactions</a> have been created"
msgstr ""
#: actions.py:93
msgid "Are you sure about closing the following bills?"
msgstr ""
#: actions.py:94
msgid ""
"Once a bill is closed it can not be further modified.</p><p>Please select a "
"payment source for the selected bills"
msgstr ""
#: actions.py:107
msgid "Close"
msgstr ""
#: actions.py:125
msgid "One bill has been sent."
msgstr ""
#: actions.py:126
#, python-format
msgid "%i bills have been sent."
msgstr ""
#: actions.py:128
msgid "Resend"
msgstr ""
#: actions.py:189
#, python-format
msgid "%(norders)s orders and %(nlines)s lines undoed."
msgstr ""
#: actions.py:208
msgid "Lines moved"
msgstr ""
#: admin.py:49 admin.py:93 admin.py:128 forms.py:11
msgid "Total"
msgstr ""
#: admin.py:80
msgid "Description"
msgstr ""
#: admin.py:88
msgid "Subtotal"
msgstr ""
#: admin.py:118
msgid "Is open"
msgstr ""
#: admin.py:123
msgid "Subline"
msgstr ""
#: admin.py:157
msgid "No bills selected."
msgstr ""
#: admin.py:164
#, python-format
msgid "Manage %s bill lines."
msgstr ""
#: admin.py:166
msgid "Bill not in open state."
msgstr ""
#: admin.py:169
msgid "Not all bills are in open state."
msgstr ""
#: admin.py:170
msgid "Manage bill lines of multiple bills."
msgstr ""
#: admin.py:190
msgid "Raw"
msgstr ""
#: admin.py:208
msgid "Created"
msgstr ""
#: admin.py:213
msgid "lines"
msgstr ""
#: admin.py:218 templates/bills/microspective.html:118
msgid "total"
msgstr ""
#: admin.py:226 models.py:88 models.py:352
msgid "type"
msgstr ""
#: admin.py:243
msgid "Payment"
msgstr ""
#: filters.py:17
msgid "All"
msgstr ""
#: filters.py:18 models.py:78
msgid "Invoice"
msgstr ""
#: filters.py:19 models.py:79
msgid "Amendment invoice"
msgstr ""
#: filters.py:20 models.py:80
msgid "Fee"
msgstr ""
#: filters.py:21
msgid "Amendment fee"
msgstr ""
#: filters.py:22
msgid "Pro-forma"
msgstr ""
#: filters.py:41
msgid "positive price"
msgstr ""
#: filters.py:46 filters.py:64
msgid "Yes"
msgstr ""
#: filters.py:47 filters.py:65
msgid "No"
msgstr ""
#: filters.py:59
msgid "has bill contact"
msgstr ""
#: forms.py:9
msgid "Number"
msgstr ""
#: forms.py:10
msgid "Account"
msgstr ""
#: forms.py:12
msgid "Type"
msgstr ""
#: forms.py:13
msgid "Source"
msgstr ""
#: helpers.py:10
msgid ""
"{relation} account \"{account}\" does not have a declared invoice contact. "
"You should <a href=\"{url}#invoicecontact-group\">provide one</a>"
msgstr ""
#: helpers.py:17
msgid "Related"
msgstr ""
#: helpers.py:24
msgid "Main"
msgstr ""
#: models.py:23 models.py:86
msgid "account"
msgstr ""
#: models.py:25
msgid "name"
msgstr ""
#: models.py:26
msgid "Account full name will be used when left blank."
msgstr ""
#: models.py:27
msgid "address"
msgstr ""
#: models.py:28
msgid "city"
msgstr ""
#: models.py:30
msgid "zip code"
msgstr ""
#: models.py:31
msgid "Enter a valid zipcode."
msgstr ""
#: models.py:32
msgid "country"
msgstr ""
#: models.py:35
msgid "VAT number"
msgstr ""
#: models.py:67
msgid "Paid"
msgstr ""
#: models.py:68
msgid "Pending"
msgstr ""
#: models.py:69
msgid "Bad debt"
msgstr ""
#: models.py:81
msgid "Amendment Fee"
msgstr ""
#: models.py:82
msgid "Pro forma"
msgstr ""
#: models.py:85
msgid "number"
msgstr ""
#: models.py:89
msgid "created on"
msgstr ""
#: models.py:90
msgid "closed on"
msgstr ""
#: models.py:91
msgid "open"
msgstr ""
#: models.py:92
msgid "sent"
msgstr ""
#: models.py:93
msgid "due on"
msgstr ""
#: models.py:94
msgid "updated on"
msgstr ""
#: models.py:97
msgid "comments"
msgstr ""
#: models.py:98
msgid "HTML"
msgstr ""
#: models.py:285
msgid "bill"
msgstr ""
#: models.py:286 models.py:350 templates/bills/microspective.html:73
msgid "description"
msgstr ""
#: models.py:287
msgid "rate"
msgstr ""
#: models.py:288
msgid "quantity"
msgstr ""
#: models.py:289
msgid "Verbose quantity"
msgstr ""
#: models.py:290 templates/bills/microspective.html:77
#: templates/bills/microspective.html:111
msgid "subtotal"
msgstr ""
#: models.py:291
msgid "tax"
msgstr ""
#: models.py:292
msgid "start"
msgstr ""
#: models.py:293
msgid "end"
msgstr ""
#: models.py:295
msgid "Informative link back to the order"
msgstr ""
#: models.py:296
msgid "order billed"
msgstr ""
#: models.py:297
msgid "order billed until"
msgstr ""
#: models.py:298
msgid "created"
msgstr ""
#: models.py:300
msgid "amended line"
msgstr ""
#: models.py:343
msgid "Volume"
msgstr ""
#: models.py:344
msgid "Compensation"
msgstr ""
#: models.py:345
msgid "Other"
msgstr ""
#: models.py:349
msgid "bill line"
msgstr ""
#: templates/bills/microspective-fee.html:107
msgid "Due date"
msgstr ""
#: templates/bills/microspective-fee.html:108
#, python-format
msgid "On %(bank_account)s"
msgstr ""
#: templates/bills/microspective-fee.html:114
#, python-format
msgid "From %(ini)s to %(end)s"
msgstr ""
#: templates/bills/microspective-fee.html:121
msgid ""
"\n"
"<strong>With your membership</strong> you are supporting ...\n"
msgstr ""
#: templates/bills/microspective.html:49
msgid "DUE DATE"
msgstr ""
#: templates/bills/microspective.html:53
msgid "TOTAL"
msgstr ""
#: templates/bills/microspective.html:57
#, python-format
msgid "%(bill_type)s DATE"
msgstr ""
#: templates/bills/microspective.html:74
msgid "period"
msgstr ""
#: templates/bills/microspective.html:75
msgid "hrs/qty"
msgstr ""
#: templates/bills/microspective.html:76
msgid "rate/price"
msgstr ""
#: templates/bills/microspective.html:111
#: templates/bills/microspective.html:114
msgid "VAT"
msgstr ""
#: templates/bills/microspective.html:114
msgid "taxes"
msgstr ""
#: templates/bills/microspective.html:130
msgid "COMMENTS"
msgstr ""
#: templates/bills/microspective.html:136
msgid "PAYMENT"
msgstr ""
#: templates/bills/microspective.html:140
#, python-format
msgid ""
"\n"
" You can pay our <i>%(type)s</i> by bank transfer.<br>\n"
" Please make sure to state your name and the <i>%(type)s</"
"i> number.\n"
" Our bank account number is <br>\n"
" "
msgstr ""
#: templates/bills/microspective.html:149
msgid "QUESTIONS"
msgstr ""
#: templates/bills/microspective.html:150
#, python-format
msgid ""
"\n"
" If you have any question about your <i>%(type)s</i>, please\n"
" feel free to write us at %(email)s. We will reply as soon as we "
"get\n"
" your message.\n"
" "
msgstr ""

View file

@ -124,6 +124,10 @@ class Bill(models.Model):
return self.PENDING
return self.BAD_DEBT
@property
def has_multiple_pages(self):
return self.type != self.FEE
def get_payment_state_display(self):
value = self.payment_state
return force_text(dict(self.PAYMENT_STATES).get(value, value))
@ -186,6 +190,7 @@ class Bill(models.Model):
def send(self):
html = self.html or self.render()
pdf = html_to_pdf(html, pagination=self.has_multiple_pages)
self.account.send_email(
template=settings.BILLS_EMAIL_NOTIFICATION_TEMPLATE,
context={
@ -195,7 +200,7 @@ class Bill(models.Model):
email_from=settings.BILLS_SELLER_EMAIL,
contacts=(Contact.BILLING,),
attachments=[
('%s.pdf' % self.number, html_to_pdf(html), 'application/pdf')
('%s.pdf' % self.number, pdf, 'application/pdf')
]
)
self.is_sent = True
@ -312,12 +317,10 @@ class BillLine(models.Model):
if self.start_on.day != 1 or self.end_on.day != 1:
date_format = "N j, 'y"
end = date(self.end_on, date_format)
# .strftime(date_format)
else:
end = date((self.end_on - datetime.timedelta(days=1)), date_format)
# ).strftime(date_format)
ini = date(self.start_on, date_format)
#.strftime(date_format)
ini = date(self.start_on, date_format).capitalize()
end = end.capitalize()
if not self.end_on:
return ini
if ini == end:

View file

@ -99,30 +99,31 @@ hr {
<div id="number" class="column-1">
<span id="number-title">{% filter title %}{% trans bill.get_type_display %}{% endfilter %}</span><br>
<span id="number-value">{{ bill.number }}</span><br>
<span id="number-date">{{ bill.closed_on | default:now | date | capfirst }}</span><br>
<span id="number-date">{{ bill.closed_on | default:now | date:"F j, Y" | capfirst }}</span><br>
</div>
<div id="amount" class="column-2">
<span id="amount-value">{{ bill.get_total }} &{{ currency.lower }};</span><br>
<span id="amount-note">{% trans "Due date" %} {{ payment.due_date| default:default_due_date | date }}<br>
<span id="amount-note">{% trans "Due date" %} {{ payment.due_date| default:default_due_date | date:"F j, Y" }}<br>
{% if not payment.message %}{% blocktrans with bank_account=seller_info.bank_account %}On {{ bank_account }}{% endblocktrans %}{% endif %}<br>
</span>
</div>
<div id="date" class="column-2">
{% blocktrans with period=bill.lines.get.get_verbose_period %}From {{ period }}{% endblocktrans %}
{% with line=bill.lines.get %}
{% blocktrans with ini=line.start_on|date:"F j, Y" end=line.end_on|date:"F j, Y" %}From {{ ini }} to {{ end }}{% endblocktrans %}
{% endwith %}
</div>
{% endblock %}
{% block content %}
<div id="text">
{% blocktrans %}
<strong>Con vuestras cuotas</strong>, ademas de obtener conexion y multitud de servicios, estais<br>
Con vuestras cuotas, ademas de obtener conexion y multitud de servicios,<br>
Con vuestras cuotas, ademas de obtener conexion <br>
Con vuestras cuotas, ademas de obtener <br>
<strong>With your membership</strong> you are supporting ...
{% endblocktrans %}
</div>
{% endblock %}
{% block footer %}
<hr>
{{ block.super }}

View file

@ -175,12 +175,12 @@ a:hover {
}
#lines .column-description {
width: 42%;
width: 40%;
text-align: left;
}
#lines .column-period {
width: 20%;
width: 22%;
}
#lines .column-quantity {

View file

@ -47,15 +47,15 @@
<hr>
<div id="due-date">
<span class="title">{% trans "DUE DATE" %}</span><br>
<psan class="value">{{ bill.due_on | default:default_due_date | date }}</span>
<psan class="value">{{ bill.due_on | default:default_due_date | date | capfirst }}</span>
</div>
<div id="total">
<span class="title">{% trans "TOTAL" %}</span><br>
<psan class="value">{{ bill.get_total }} &{{ currency.lower }};</span>
</div>
<div id="bill-date">
<span class="title">{% blocktrans with bill_type=bill.get_type_display.upper %}{{ bill_type }} DATE {% endblocktrans %}</span><br>
<psan class="value">{{ bill.closed_on | default:now | date }}</span>
<span class="title">{% blocktrans with bill_type=bill.get_type_display.upper %}{{ bill_type }} DATE{% endblocktrans %}</span><br>
<psan class="value">{{ bill.closed_on | default:now | date | capfirst }}</span>
</div>
</div>
<div id="buyer-details">
@ -77,9 +77,9 @@
<span class="title column-subtotal">{% trans "subtotal" %}</span>
<br>
{% for line in lines %}
{% with sublines=line.sublines.all description=line.description|slice:"40:" %}
{% with sublines=line.sublines.all description=line.description|slice:"38:" %}
<span class="{% if not sublines and not description %}last {% endif %}column-id">{% if not line.order_id %}L{% endif %}{{ line.order_id }}</span>
<span class="{% if not sublines and not description %}last {% endif %}column-description">{{ line.description|slice:":40" }}</span>
<span class="{% if not sublines and not description %}last {% endif %}column-description">{{ line.description|slice:":38" }}</span>
<span class="{% if not sublines and not description %}last {% endif %}column-period">{{ line.get_verbose_period }}</span>
<span class="{% if not sublines and not description %}last {% endif %}column-quantity">{{ line.get_verbose_quantity|default:"&nbsp;"|safe }}</span>
<span class="{% if not sublines and not description %}last {% endif %}column-rate">{% if line.rate %}{{ line.rate }} &{{ currency.lower }};{% else %}&nbsp;{% endif %}</span>
@ -87,7 +87,7 @@
<br>
{% if description %}
<span class="{% if not sublines %}last {% endif %}subline column-id">&nbsp;</span>
<span class="{% if not sublines %}last {% endif %}subline column-description">{{ description|truncatechars:41 }}</span>
<span class="{% if not sublines %}last {% endif %}subline column-description">{{ description|truncatechars:39 }}</span>
<span class="{% if not sublines %}last {% endif %}subline column-period">&nbsp;</span>
<span class="{% if not sublines %}last {% endif %}subline column-quantity">&nbsp;</span>
<span class="{% if not sublines %}last {% endif %}subline column-rate">&nbsp;</span>
@ -95,7 +95,7 @@
{% endif %}
{% for subline in sublines %}
<span class="{% if forloop.last %}last {% endif %}subline column-id">&nbsp;</span>
<span class="{% if forloop.last %}last {% endif %}subline column-description">{{ subline.description|truncatechars:41 }}</span>
<span class="{% if forloop.last %}last {% endif %}subline column-description">{{ subline.description|truncatechars:39 }}</span>
<span class="{% if forloop.last %}last {% endif %}subline column-period">&nbsp;</span>
<span class="{% if forloop.last %}last {% endif %}subline column-quantity">&nbsp;</span>
<span class="{% if forloop.last %}last {% endif %}subline column-rate">&nbsp;</span>
@ -138,7 +138,7 @@
{{ payment.message | safe }}
{% else %}
{% blocktrans with type=bill.get_type_display.lower %}
You can pay our <i>{{ type }}</i> by bank transfer. <br>
You can pay our <i>{{ type }}</i> by bank transfer.<br>
Please make sure to state your name and the <i>{{ type }}</i> number.
Our bank account number is <br>
{% endblocktrans %}
@ -147,9 +147,9 @@
</div>
<div id="questions">
<span class="title">{% trans "QUESTIONS" %}</span>
{% blocktrans with type=bill.get_type_display.lower %}
{% blocktrans with type=bill.get_type_display.lower email=seller_info.email %}
If you have any question about your <i>{{ type }}</i>, please
feel free to contact us at your convinience. We will reply as soon as we get
feel free to write us at {{ email }}. We will reply as soon as we get
your message.
{% endblocktrans %}
</div>

View file

@ -21,7 +21,7 @@ class RecordSerializer(serializers.ModelSerializer):
class DomainSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
""" Validates if this zone generates a correct zone file """
records = RecordSerializer(required=False, many=True) #allow_add_remove=True)
records = RecordSerializer(required=False, many=True)
class Meta:
model = Domain
@ -44,3 +44,35 @@ class DomainSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
domain = domain_for_validation(self.instance, records)
validators.validate_zone(domain.render_zone())
return data
def create(self, validated_data):
records = validated_data.pop('records')
domain = super(DomainSerializer, self).create(validated_data)
for record in records:
domain.records.create(type=record['type'], value=record['value'])
return domain
def update(self, validated_data):
precords = validated_data.pop('records')
domain = super(DomainSerializer, self).update(validated_data)
to_delete = []
for erecord in domain.records.all():
match = False
for ix, precord in enumerate(precords):
if erecord.type == precord['type'] and erecord.value == precord['value']:
match = True
break
if match:
precords.remove(ix)
else:
to_delete.append(erecord)
for precord in precords:
try:
recycled = to_delete.pop()
except IndexError:
domain.records.create(type=precord['type'], value=precord['value'])
else:
recycled.type = precord['type']
recycled.value = precord['value']
recycled.save()
return domain

View file

@ -92,7 +92,7 @@ class BillSelectedOrders(object):
url = change_url(bills[0])
else:
url = reverse('admin:bills_bill_changelist')
ids = ','.join(map(str, bills))
ids = ','.join([str(b.id) for b in bills])
url += '?id__in=%s' % ids
msg = ungettext(
'<a href="{url}">One bill</a> has been created.',

View file

@ -245,6 +245,10 @@ class ServiceHandler(plugins.Plugin, metaclass=plugins.PluginMount):
}))
def generate_line(self, order, price, *dates, metric=1, discounts=None, computed=False):
"""
discounts: already applied discounts on price
computed: price = price*size already performed
"""
if len(dates) == 2:
ini, end = dates
elif len(dates) == 1:
@ -377,7 +381,8 @@ class ServiceHandler(plugins.Plugin, metaclass=plugins.PluginMount):
size = self.get_price_size(order.new_billed_until, new_end)
price += price*size
order.new_billed_until = new_end
line = self.generate_line(order, price, ini, new_end or end, discounts=discounts, computed=True)
line = self.generate_line(
order, price, ini, new_end or end, discounts=discounts, computed=True)
lines.append(line)
return lines

View file

@ -3,17 +3,24 @@ import textwrap
from orchestra.utils.sys import run
def html_to_pdf(html):
def html_to_pdf(html, pagination=False):
""" converts HTL to PDF using wkhtmltopdf """
return run(textwrap.dedent("""\
PATH=$PATH:/usr/local/bin/
xvfb-run -a -s "-screen 0 2480x3508x16" wkhtmltopdf -q \\
--use-xserver \\
print(pagination)
context = {
'pagination': textwrap.dedent("""\
--footer-center "Page [page] of [topage]"\\
--footer-center "Page [page] of [topage]" \\
--footer-font-name sans \\
--footer-font-size 7 \\
--footer-spacing 7 \\
--footer-spacing 7"""
) if pagination else '',
}
cmd = textwrap.dedent("""\
PATH=$PATH:/usr/local/bin/
xvfb-run -a -s "-screen 0 2480x3508x16" wkhtmltopdf -q \\
--use-xserver \\
%(pagination)s \\
--margin-bottom 22 \\
--margin-top 20 - - """),
stdin=html.encode('utf-8')
).stdout
--margin-top 20 - -\
""") % context
return run(cmd, stdin=html.encode('utf-8')).stdout