Preliminar implementation of amended invoices
This commit is contained in:
parent
3520b3968b
commit
75c72ce8a5
|
@ -17,6 +17,7 @@ from orchestra.admin.forms import adminmodelformset_factory
|
|||
from orchestra.admin.utils import get_object_from_url, change_url
|
||||
from orchestra.utils.html import html_to_pdf
|
||||
|
||||
from . import settings
|
||||
from .forms import SelectSourceForm
|
||||
from .helpers import validate_contact
|
||||
from .models import Bill, BillLine
|
||||
|
@ -217,7 +218,7 @@ def amend_bills(modeladmin, request, queryset):
|
|||
if queryset.filter(is_open=True).exists():
|
||||
messages.warning(request, _("Selected bills should be in closed state"))
|
||||
return
|
||||
ids = []
|
||||
amend_ids = []
|
||||
for bill in queryset:
|
||||
with translation.override(bill.account.language):
|
||||
amend_type = bill.get_amend_type()
|
||||
|
@ -239,17 +240,33 @@ def amend_bills(modeladmin, request, queryset):
|
|||
line = BillLine.objects.create(
|
||||
bill=amend,
|
||||
start_on=bill.created_on,
|
||||
description=_("Amend of %(related_type)s %(number)s, tax %(tax)s%%") % context,
|
||||
description=_("%(related_type)s %(number)s subtotal for tax %(tax)s%%") % context,
|
||||
subtotal=subtotals[0],
|
||||
tax=tax
|
||||
)
|
||||
ids.append(bill.pk)
|
||||
amend_url = reverse('admin:bills_bill_changelist')
|
||||
amend_url += '?id=%s' % ','.join(map(str, ids))
|
||||
amend_ids.append(amend.pk)
|
||||
num = len(amend_ids)
|
||||
if num == 1:
|
||||
amend_url = reverse('admin:bills_bill_change', args=amend_ids)
|
||||
else:
|
||||
amend_url = reverse('admin:bills_bill_changelist')
|
||||
amend_url += '?id=%s' % ','.join(map(str, amend_ids))
|
||||
context = {
|
||||
'url': amend_url,
|
||||
'num': num,
|
||||
}
|
||||
messages.success(request, mark_safe(ungettext(
|
||||
_('<a href="%s">One amendment bill</a> have been generated.') % amend_url,
|
||||
_('<a href="%s">%i amendment bills</a> have been generated.') % (amend_url, len(ids)),
|
||||
len(ids)
|
||||
_('<a href="%(url)s">One amendment bill</a> have been generated.') % context,
|
||||
_('<a href="%(url)s">%(num)i amendment bills</a> have been generated.') % context,
|
||||
num
|
||||
)))
|
||||
amend_bills.verbose_name = _("Amend")
|
||||
amend_bills.url_name = 'amend'
|
||||
|
||||
|
||||
def report(modeladmin, request, queryset):
|
||||
context = {
|
||||
'bills': queryset,
|
||||
'currency': settings.BILLS_CURRENCY,
|
||||
}
|
||||
return render(request, 'admin/bills/report.html', context)
|
||||
|
|
|
@ -186,10 +186,10 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
|||
'num_lines', 'display_total', 'display_payment_state', 'is_open', 'is_sent'
|
||||
)
|
||||
list_filter = (BillTypeListFilter, 'is_open', 'is_sent', TotalListFilter, PaymentStateListFilter)
|
||||
add_fields = ('account', 'type', 'is_open', 'due_on', 'comments')
|
||||
add_fields = ('account', 'type', 'amend_of', 'is_open', 'due_on', 'comments')
|
||||
fieldsets = (
|
||||
(None, {
|
||||
'fields': ('number', 'type', 'account_link', 'display_total',
|
||||
'fields': ('number', 'type', 'amend_of_link', 'account_link', 'display_total',
|
||||
'display_payment_state', 'is_sent', 'due_on', 'comments'),
|
||||
}),
|
||||
(_("Raw"), {
|
||||
|
@ -205,13 +205,14 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
|||
]
|
||||
actions = [
|
||||
actions.manage_lines, actions.download_bills, actions.close_bills, actions.send_bills,
|
||||
actions.amend_bills,
|
||||
actions.amend_bills, actions.report
|
||||
]
|
||||
change_readonly_fields = ('account_link', 'type', 'is_open')
|
||||
change_readonly_fields = ('account_link', 'type', 'is_open', 'amend_of_link')
|
||||
readonly_fields = ('number', 'display_total', 'is_sent', 'display_payment_state')
|
||||
inlines = [BillLineInline, ClosedBillLineInline]
|
||||
|
||||
created_on_display = admin_date('created_on', short_description=_("Created"))
|
||||
amend_of_link = admin_link('amend_of')
|
||||
|
||||
def num_lines(self, bill):
|
||||
return bill.lines__count
|
||||
|
@ -267,8 +268,12 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
|||
|
||||
def get_fieldsets(self, request, obj=None):
|
||||
fieldsets = super(BillAdmin, self).get_fieldsets(request, obj)
|
||||
if obj and obj.is_open:
|
||||
fieldsets = (fieldsets[0],)
|
||||
if obj:
|
||||
# if obj.amend_of_id:
|
||||
# fieldsets = list(fieldsets)
|
||||
# fieldsets[0][1]['fields'] = fieldsets[0][1]['fields'] + ('amend_of_link',)
|
||||
if obj.is_open:
|
||||
fieldsets = (fieldsets[0],)
|
||||
return fieldsets
|
||||
|
||||
def get_change_view_actions(self, obj=None):
|
||||
|
@ -289,10 +294,13 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
|||
""" Make value input widget bigger """
|
||||
if db_field.name == 'comments':
|
||||
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 4})
|
||||
if db_field.name == 'html':
|
||||
elif db_field.name == 'html':
|
||||
kwargs['widget'] = forms.Textarea(attrs={'cols': 150, 'rows': 20})
|
||||
return super(BillAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
||||
|
||||
formfield = super(BillAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
||||
if db_field.name == 'amend_of':
|
||||
formfield.queryset = formfield.queryset.filter(is_open=False)
|
||||
return formfield
|
||||
|
||||
def get_queryset(self, request):
|
||||
qs = super(BillAdmin, self).get_queryset(request)
|
||||
qs = qs.annotate(
|
||||
|
|
|
@ -4,6 +4,8 @@ from django.db.models import Q
|
|||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from . models import Bill
|
||||
|
||||
|
||||
class BillTypeListFilter(SimpleListFilter):
|
||||
""" Filter tickets by created_by according to request.user """
|
||||
|
@ -89,6 +91,7 @@ class PaymentStateListFilter(SimpleListFilter):
|
|||
('PAID', _("Paid")),
|
||||
('PENDING', _("Pending")),
|
||||
('BAD_DEBT', _("Bad debt")),
|
||||
('AMENDED', _("Amended")),
|
||||
)
|
||||
|
||||
def queryset(self, request, queryset):
|
||||
|
@ -96,10 +99,12 @@ class PaymentStateListFilter(SimpleListFilter):
|
|||
if self.value() == 'OPEN':
|
||||
return queryset.filter(Q(is_open=True)|Q(type=queryset.model.PROFORMA))
|
||||
elif self.value() == 'PAID':
|
||||
zeros = queryset.filter(computed_total=0, computed_total__isnull=True).values_list('id', flat=True)
|
||||
zeros = queryset.filter(computed_total=0, computed_total__isnull=True)
|
||||
zeros = zeros.values_list('id', flat=True)
|
||||
ammounts = Transaction.objects.exclude(bill_id__in=zeros).secured().group_by('bill_id')
|
||||
paid = []
|
||||
for bill_id, total in queryset.exclude(computed_total=0, computed_total__isnull=True, is_open=True).values_list('id', 'computed_total'):
|
||||
relevant = queryset.exclude(computed_total=0, computed_total__isnull=True, is_open=True)
|
||||
for bill_id, total in relevant.values_list('id', 'computed_total'):
|
||||
try:
|
||||
ammount = sum([t.ammount for t in ammounts[bill_id]])
|
||||
except KeyError:
|
||||
|
@ -107,7 +112,11 @@ class PaymentStateListFilter(SimpleListFilter):
|
|||
else:
|
||||
if abs(total) <= abs(ammount):
|
||||
paid.append(bill_id)
|
||||
return queryset.filter(Q(computed_total=0)|Q(computed_total__isnull=True)|Q(id__in=paid)).exclude(is_open=True)
|
||||
return queryset.filter(
|
||||
Q(computed_total=0) |
|
||||
Q(computed_total__isnull=True) |
|
||||
Q(id__in=paid)
|
||||
).exclude(is_open=True)
|
||||
elif self.value() == 'PENDING':
|
||||
has_transaction = queryset.exclude(transactions__isnull=True)
|
||||
non_rejected = has_transaction.exclude(transactions__state=Transaction.REJECTED)
|
||||
|
@ -115,4 +124,11 @@ class PaymentStateListFilter(SimpleListFilter):
|
|||
return queryset.filter(pk__in=non_rejected)
|
||||
elif self.value() == 'BAD_DEBT':
|
||||
closed = queryset.filter(is_open=False).exclude(computed_total=0)
|
||||
return closed.filter(Q(transactions__state=Transaction.REJECTED)|Q(transactions__isnull=True))
|
||||
return closed.filter(
|
||||
Q(transactions__state=Transaction.REJECTED) |
|
||||
Q(transactions__isnull=True)
|
||||
)
|
||||
elif self.value() == 'AMENDED':
|
||||
amendeds = queryset.filter(type__in=Bill.AMEND_MAP.values(), is_open=False)
|
||||
amendeds_ids = amendeds.values_list('amend_of', flat=True)
|
||||
return queryset.filter(id__in=amendeds)
|
||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-05-29 09:39+0000\n"
|
||||
"POT-Creation-Date: 2015-07-07 10:18+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"
|
||||
|
@ -18,37 +18,37 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: actions.py:37
|
||||
#: actions.py:40
|
||||
msgid "Download"
|
||||
msgstr "Descarrega"
|
||||
|
||||
#: actions.py:47
|
||||
#: actions.py:50
|
||||
msgid "View"
|
||||
msgstr "Vista"
|
||||
|
||||
#: actions.py:55
|
||||
#: actions.py:58
|
||||
msgid "Selected bills should be in open state"
|
||||
msgstr "Les factures seleccionades han d'estar en estat obert"
|
||||
|
||||
#: actions.py:73
|
||||
#: actions.py:76
|
||||
msgid "Selected bills have been closed"
|
||||
msgstr "Les factures seleccionades han estat tancades"
|
||||
|
||||
#: actions.py:86
|
||||
#: actions.py:89
|
||||
#, python-format
|
||||
msgid "<a href=\"%(url)s\">One related transaction</a> has been created"
|
||||
msgstr "S'ha creat una <a href=\"%(url)s\">transacció</a>"
|
||||
|
||||
#: actions.py:87
|
||||
#: actions.py:90
|
||||
#, python-format
|
||||
msgid "<a href=\"%(url)s\">%(num)i related transactions</a> have been created"
|
||||
msgstr "S'han creat les <a href=\"%(url)s\">%(num)i següents transaccions</a>"
|
||||
|
||||
#: actions.py:93
|
||||
#: actions.py:96
|
||||
msgid "Are you sure about closing the following bills?"
|
||||
msgstr "Estàs a punt de tancar les següents factures, estàs segur?"
|
||||
|
||||
#: actions.py:94
|
||||
#: actions.py:97
|
||||
msgid ""
|
||||
"Once a bill is closed it can not be further modified.</p><p>Please select a "
|
||||
"payment source for the selected bills"
|
||||
|
@ -56,138 +56,183 @@ msgstr ""
|
|||
"Una vegada la factura estigui tancada no podrà ser modificada.</p><p>Si us "
|
||||
"plau selecciona un mètode de pagament per les factures seleccionades"
|
||||
|
||||
#: actions.py:107
|
||||
#: actions.py:110
|
||||
msgid "Close"
|
||||
msgstr "Tanca"
|
||||
|
||||
#: actions.py:125
|
||||
#: actions.py:124
|
||||
msgid "One bill has been sent."
|
||||
msgstr "S'ha creat una factura"
|
||||
|
||||
#: actions.py:126
|
||||
#: actions.py:125
|
||||
#, python-format
|
||||
msgid "%i bills have been sent."
|
||||
msgstr "S'han enviat %i factures."
|
||||
|
||||
#: actions.py:128
|
||||
#: actions.py:127
|
||||
msgid "Resend"
|
||||
msgstr "Reenviat"
|
||||
|
||||
#: actions.py:189
|
||||
#: actions.py:188
|
||||
#, python-format
|
||||
msgid "%(norders)s orders and %(nlines)s lines undoed."
|
||||
msgstr "%(norders)s ordres i %(nlines)s línies desfetes."
|
||||
|
||||
#: actions.py:208
|
||||
#: actions.py:207
|
||||
msgid "Lines moved"
|
||||
msgstr "Línies mogudes"
|
||||
|
||||
#: admin.py:49 admin.py:93 admin.py:128 forms.py:11
|
||||
#: actions.py:219
|
||||
msgid "Selected bills should be in closed state"
|
||||
msgstr "Les factures seleccionades han d'estar en estat obert"
|
||||
|
||||
#: actions.py:236
|
||||
#, python-format
|
||||
msgid "%(type)s of %(related_type)s %(number)s and creation date %(date)s"
|
||||
msgstr "%(type)s de %(related_type)s %(number)s amb data de creació %(date)s"
|
||||
|
||||
#: actions.py:243
|
||||
#, python-format
|
||||
msgid "%(related_type)s %(number)s subtotal for tax %(tax)s%%"
|
||||
msgstr "%(related_type)s %(number)s subtotal %(tax)s%%"
|
||||
|
||||
#: actions.py:259
|
||||
#, python-format
|
||||
msgid "<a href=\"%(url)s\">One amendment bill</a> have been generated."
|
||||
msgstr "S'ha creat una <a href=\"%(url)s\">transacció</a>"
|
||||
|
||||
#: actions.py:260
|
||||
#, python-format
|
||||
msgid "<a href=\"%(url)s\">%(num)i amendment bills</a> have been generated."
|
||||
msgstr "S'han creat les <a href=\"%(url)s\">%(num)i següents transaccions</a>"
|
||||
|
||||
#: actions.py:263
|
||||
msgid "Amend"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:54 admin.py:98 admin.py:133 forms.py:11
|
||||
#: templates/admin/bills/report.html:43
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: admin.py:80
|
||||
#: admin.py:85
|
||||
msgid "Description"
|
||||
msgstr "Descripció"
|
||||
|
||||
#: admin.py:88
|
||||
#: admin.py:93
|
||||
msgid "Subtotal"
|
||||
msgstr "Subtotal"
|
||||
|
||||
#: admin.py:118
|
||||
#: admin.py:123
|
||||
msgid "Is open"
|
||||
msgstr "És oberta"
|
||||
|
||||
#: admin.py:123
|
||||
#: admin.py:128
|
||||
msgid "Subline"
|
||||
msgstr "Sublínia"
|
||||
|
||||
#: admin.py:157
|
||||
#: admin.py:162
|
||||
msgid "No bills selected."
|
||||
msgstr "No hi ha factures seleccionades"
|
||||
|
||||
#: admin.py:164
|
||||
#: admin.py:169
|
||||
#, python-format
|
||||
msgid "Manage %s bill lines."
|
||||
msgstr "Gestiona %s línies de factura."
|
||||
|
||||
#: admin.py:166
|
||||
#: admin.py:171
|
||||
msgid "Bill not in open state."
|
||||
msgstr "La factura no està en estat obert"
|
||||
|
||||
#: admin.py:169
|
||||
#: admin.py:174
|
||||
msgid "Not all bills are in open state."
|
||||
msgstr "No totes les factures estan en estat obert"
|
||||
|
||||
#: admin.py:170
|
||||
#: admin.py:175
|
||||
msgid "Manage bill lines of multiple bills."
|
||||
msgstr "Gestiona línies de factura de multiples factures."
|
||||
|
||||
#: admin.py:190
|
||||
#: admin.py:195
|
||||
msgid "Raw"
|
||||
msgstr "Raw"
|
||||
|
||||
#: admin.py:208
|
||||
#: admin.py:214 models.py:72
|
||||
msgid "Created"
|
||||
msgstr "Creada"
|
||||
|
||||
#: admin.py:213
|
||||
#: admin.py:220
|
||||
msgid "lines"
|
||||
msgstr "línies"
|
||||
|
||||
#: admin.py:218 templates/bills/microspective.html:118
|
||||
#: admin.py:225 filters.py:44 templates/bills/microspective.html:118
|
||||
msgid "total"
|
||||
msgstr "total"
|
||||
|
||||
#: admin.py:226 models.py:88 models.py:352
|
||||
#: admin.py:233 models.py:103 models.py:446
|
||||
msgid "type"
|
||||
msgstr "tipus"
|
||||
|
||||
#: admin.py:243
|
||||
#: admin.py:250
|
||||
msgid "Payment"
|
||||
msgstr "Pagament"
|
||||
|
||||
#: filters.py:17
|
||||
#: filters.py:19
|
||||
msgid "All"
|
||||
msgstr "Tot"
|
||||
|
||||
#: filters.py:18 models.py:78
|
||||
#: filters.py:20 models.py:87
|
||||
msgid "Invoice"
|
||||
msgstr "Factura"
|
||||
|
||||
#: filters.py:19 models.py:79
|
||||
#: filters.py:21 models.py:88
|
||||
msgid "Amendment invoice"
|
||||
msgstr "Factura rectificativa"
|
||||
|
||||
#: filters.py:20 models.py:80
|
||||
#: filters.py:22 models.py:89
|
||||
msgid "Fee"
|
||||
msgstr "Quota de soci"
|
||||
|
||||
#: filters.py:21
|
||||
#: filters.py:23
|
||||
msgid "Amendment fee"
|
||||
msgstr "Rectificació de quota de soci"
|
||||
|
||||
#: filters.py:22
|
||||
#: filters.py:24
|
||||
msgid "Pro-forma"
|
||||
msgstr "Pro-forma"
|
||||
|
||||
#: filters.py:41
|
||||
msgid "positive price"
|
||||
msgstr "preu positiu"
|
||||
|
||||
#: filters.py:46 filters.py:64
|
||||
msgid "Yes"
|
||||
msgstr "Si"
|
||||
|
||||
#: filters.py:47 filters.py:65
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
||||
#: filters.py:59
|
||||
#: filters.py:66
|
||||
msgid "has bill contact"
|
||||
msgstr "té contacte de facturació"
|
||||
|
||||
#: forms.py:9
|
||||
#: filters.py:71
|
||||
msgid "Yes"
|
||||
msgstr "Si"
|
||||
|
||||
#: filters.py:72
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
||||
#: filters.py:83
|
||||
msgid "payment state"
|
||||
msgstr "Pagament"
|
||||
|
||||
#: filters.py:88 models.py:71
|
||||
msgid "Open"
|
||||
msgstr ""
|
||||
|
||||
#: filters.py:89 models.py:75
|
||||
msgid "Paid"
|
||||
msgstr "Pagat"
|
||||
|
||||
#: filters.py:90
|
||||
msgid "Pending"
|
||||
msgstr "Pendent"
|
||||
|
||||
#: filters.py:91 models.py:78
|
||||
msgid "Bad debt"
|
||||
msgstr "Incobrable"
|
||||
|
||||
#: forms.py:9 templates/admin/bills/report.html:37
|
||||
msgid "Number"
|
||||
msgstr "Número"
|
||||
|
||||
|
@ -219,7 +264,7 @@ msgstr "Relacionat"
|
|||
msgid "Main"
|
||||
msgstr "Principal"
|
||||
|
||||
#: models.py:23 models.py:86
|
||||
#: models.py:23 models.py:99
|
||||
msgid "account"
|
||||
msgstr "compte"
|
||||
|
||||
|
@ -251,141 +296,186 @@ msgstr "Introdueix un codi postal vàlid."
|
|||
msgid "country"
|
||||
msgstr "país"
|
||||
|
||||
#: models.py:35
|
||||
#: models.py:35 templates/admin/bills/report.html:38
|
||||
msgid "VAT number"
|
||||
msgstr "NIF"
|
||||
|
||||
#: models.py:67
|
||||
msgid "Paid"
|
||||
msgstr "Pagat"
|
||||
#: models.py:73
|
||||
msgid "Processed"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:68
|
||||
msgid "Pending"
|
||||
msgstr "Pendent"
|
||||
#: models.py:74
|
||||
#, fuzzy
|
||||
#| msgid "amended line"
|
||||
msgid "Amended"
|
||||
msgstr "línia rectificada"
|
||||
|
||||
#: models.py:69
|
||||
msgid "Bad debt"
|
||||
msgstr "Incobrable"
|
||||
#: models.py:76
|
||||
msgid "Incomplete"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:81
|
||||
#: models.py:77
|
||||
msgid "Executed"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:90
|
||||
msgid "Amendment Fee"
|
||||
msgstr "Rectificació de quota de soci"
|
||||
|
||||
#: models.py:82
|
||||
#: models.py:91
|
||||
msgid "Pro forma"
|
||||
msgstr "Pro forma"
|
||||
|
||||
#: models.py:85
|
||||
#: models.py:98
|
||||
msgid "number"
|
||||
msgstr "número"
|
||||
|
||||
#: models.py:89
|
||||
#: models.py:101
|
||||
#, fuzzy
|
||||
#| msgid "amended line"
|
||||
msgid "amend of"
|
||||
msgstr "línia rectificada"
|
||||
|
||||
#: models.py:104
|
||||
msgid "created on"
|
||||
msgstr "creat el"
|
||||
|
||||
#: models.py:90
|
||||
#: models.py:105
|
||||
msgid "closed on"
|
||||
msgstr "tancat el"
|
||||
|
||||
#: models.py:91
|
||||
#: models.py:106
|
||||
msgid "open"
|
||||
msgstr "obert"
|
||||
|
||||
#: models.py:92
|
||||
#: models.py:107
|
||||
msgid "sent"
|
||||
msgstr "enviat"
|
||||
|
||||
#: models.py:93
|
||||
#: models.py:108
|
||||
msgid "due on"
|
||||
msgstr "es deu"
|
||||
|
||||
#: models.py:94
|
||||
#: models.py:109
|
||||
msgid "updated on"
|
||||
msgstr "actualitzada el"
|
||||
|
||||
#: models.py:97
|
||||
#: models.py:112
|
||||
msgid "comments"
|
||||
msgstr "comentaris"
|
||||
|
||||
#: models.py:98
|
||||
#: models.py:113
|
||||
msgid "HTML"
|
||||
msgstr "HTML"
|
||||
|
||||
#: models.py:285
|
||||
#: models.py:192
|
||||
#, python-format
|
||||
msgid "Type %s is not an amendment."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:194
|
||||
msgid "Amend of related account doesn't match bill account."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:199
|
||||
#, python-format
|
||||
msgid "Type %s requires an amend of link."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:378
|
||||
msgid "bill"
|
||||
msgstr "factura"
|
||||
|
||||
#: models.py:286 models.py:350 templates/bills/microspective.html:73
|
||||
#: models.py:379 models.py:444 templates/bills/microspective.html:73
|
||||
msgid "description"
|
||||
msgstr "descripció"
|
||||
|
||||
#: models.py:287
|
||||
#: models.py:380
|
||||
msgid "rate"
|
||||
msgstr "tarifa"
|
||||
|
||||
#: models.py:288
|
||||
#: models.py:381
|
||||
msgid "quantity"
|
||||
msgstr "quantitat"
|
||||
|
||||
#: models.py:289
|
||||
#: models.py:383
|
||||
#, fuzzy
|
||||
#| msgid "quantity"
|
||||
msgid "Verbose quantity"
|
||||
msgstr "quantitat"
|
||||
|
||||
#: models.py:290 templates/bills/microspective.html:77
|
||||
#: models.py:384 templates/bills/microspective.html:77
|
||||
#: templates/bills/microspective.html:111
|
||||
msgid "subtotal"
|
||||
msgstr "subtotal"
|
||||
|
||||
#: models.py:291
|
||||
#: models.py:385
|
||||
msgid "tax"
|
||||
msgstr "impostos"
|
||||
|
||||
#: models.py:292
|
||||
#: models.py:386
|
||||
msgid "start"
|
||||
msgstr "iniciar"
|
||||
|
||||
#: models.py:293
|
||||
#: models.py:387
|
||||
msgid "end"
|
||||
msgstr "finalitzar"
|
||||
|
||||
#: models.py:295
|
||||
#: models.py:389
|
||||
msgid "Informative link back to the order"
|
||||
msgstr "Enllaç informatiu de l'ordre"
|
||||
|
||||
#: models.py:296
|
||||
#: models.py:390
|
||||
msgid "order billed"
|
||||
msgstr "ordre facturada"
|
||||
|
||||
#: models.py:297
|
||||
#: models.py:391
|
||||
msgid "order billed until"
|
||||
msgstr "ordre facturada fins a"
|
||||
|
||||
#: models.py:298
|
||||
#: models.py:392
|
||||
msgid "created"
|
||||
msgstr "creada"
|
||||
|
||||
#: models.py:300
|
||||
#: models.py:394
|
||||
msgid "amended line"
|
||||
msgstr "línia rectificada"
|
||||
|
||||
#: models.py:343
|
||||
#: models.py:437
|
||||
msgid "Volume"
|
||||
msgstr "Volum"
|
||||
|
||||
#: models.py:344
|
||||
#: models.py:438
|
||||
msgid "Compensation"
|
||||
msgstr "Compensació"
|
||||
|
||||
#: models.py:345
|
||||
#: models.py:439
|
||||
msgid "Other"
|
||||
msgstr "Altre"
|
||||
|
||||
#: models.py:349
|
||||
#: models.py:443
|
||||
msgid "bill line"
|
||||
msgstr "línia de factura"
|
||||
|
||||
#: templates/admin/bills/report.html:39
|
||||
msgid "Contact"
|
||||
msgstr ""
|
||||
|
||||
#: templates/admin/bills/report.html:40
|
||||
#, fuzzy
|
||||
#| msgid "Due date"
|
||||
msgid "Close date"
|
||||
msgstr "Data de pagament"
|
||||
|
||||
#: templates/admin/bills/report.html:41
|
||||
msgid "Base"
|
||||
msgstr ""
|
||||
|
||||
#: templates/admin/bills/report.html:42 templates/bills/microspective.html:111
|
||||
#: templates/bills/microspective.html:114
|
||||
msgid "VAT"
|
||||
msgstr "IVA"
|
||||
|
||||
#: templates/bills/microspective-fee.html:107
|
||||
msgid "Due date"
|
||||
msgstr "Data de pagament"
|
||||
|
@ -433,11 +523,6 @@ msgstr "hrs/qnt"
|
|||
msgid "rate/price"
|
||||
msgstr "tarifa/preu"
|
||||
|
||||
#: templates/bills/microspective.html:111
|
||||
#: templates/bills/microspective.html:114
|
||||
msgid "VAT"
|
||||
msgstr "IVA"
|
||||
|
||||
#: templates/bills/microspective.html:114
|
||||
msgid "taxes"
|
||||
msgstr "impostos"
|
||||
|
@ -451,6 +536,7 @@ msgid "PAYMENT"
|
|||
msgstr "PAGAMENT"
|
||||
|
||||
#: templates/bills/microspective.html:140
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" You can pay our <i>%(type)s</i> by bank transfer.<br>\n"
|
||||
|
@ -483,3 +569,6 @@ msgstr ""
|
|||
" contacta amb nosaltres a %(email)s. Et respondrem el més "
|
||||
"ràpidament possible.\n"
|
||||
" "
|
||||
|
||||
#~ msgid "positive price"
|
||||
#~ msgstr "preu positiu"
|
||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-05-28 12:31+0000\n"
|
||||
"POT-Creation-Date: 2015-07-07 10:10+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"
|
||||
|
@ -18,37 +18,37 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: actions.py:37
|
||||
#: actions.py:40
|
||||
msgid "Download"
|
||||
msgstr "Descarga"
|
||||
|
||||
#: actions.py:47
|
||||
#: actions.py:50
|
||||
msgid "View"
|
||||
msgstr "Vista"
|
||||
|
||||
#: actions.py:55
|
||||
#: actions.py:58
|
||||
msgid "Selected bills should be in open state"
|
||||
msgstr "Las facturas seleccionadas están en estado abierto"
|
||||
|
||||
#: actions.py:73
|
||||
#: actions.py:76
|
||||
msgid "Selected bills have been closed"
|
||||
msgstr "Las facturas seleccionadas han sido cerradas"
|
||||
|
||||
#: actions.py:86
|
||||
#: actions.py:89
|
||||
#, python-format
|
||||
msgid "<a href=\"%(url)s\">One related transaction</a> has been created"
|
||||
msgstr "Se ha creado una <a href=\"%(url)s\">transacción</a>"
|
||||
|
||||
#: actions.py:87
|
||||
#: actions.py:90
|
||||
#, python-format
|
||||
msgid "<a href=\"%(url)s\">%(num)i related transactions</a> have been created"
|
||||
msgstr "Se han creado <a href=\"%(url)s\">%(num)i transacciones</a>"
|
||||
|
||||
#: actions.py:93
|
||||
#: actions.py:96
|
||||
msgid "Are you sure about closing the following bills?"
|
||||
msgstr "Estás a punto de cerrar las sigüientes facturas. ¿Estás seguro?"
|
||||
|
||||
#: actions.py:94
|
||||
#: actions.py:97
|
||||
msgid ""
|
||||
"Once a bill is closed it can not be further modified.</p><p>Please select a "
|
||||
"payment source for the selected bills"
|
||||
|
@ -56,138 +56,180 @@ msgstr ""
|
|||
"Una vez cerrada la factura ya no se podrá modificar.</p><p>Por favor "
|
||||
"seleciona un metodo de pago para las facturas seleccionadas"
|
||||
|
||||
#: actions.py:107
|
||||
#: actions.py:110
|
||||
msgid "Close"
|
||||
msgstr "Cerrar"
|
||||
|
||||
#: actions.py:125
|
||||
#: actions.py:124
|
||||
msgid "One bill has been sent."
|
||||
msgstr "Se ha enviado una factura"
|
||||
|
||||
#: actions.py:126
|
||||
#: actions.py:125
|
||||
#, python-format
|
||||
msgid "%i bills have been sent."
|
||||
msgstr ""
|
||||
|
||||
#: actions.py:128
|
||||
#: actions.py:127
|
||||
msgid "Resend"
|
||||
msgstr ""
|
||||
|
||||
#: actions.py:189
|
||||
#: actions.py:188
|
||||
#, python-format
|
||||
msgid "%(norders)s orders and %(nlines)s lines undoed."
|
||||
msgstr ""
|
||||
|
||||
#: actions.py:208
|
||||
#: actions.py:207
|
||||
msgid "Lines moved"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:49 admin.py:93 admin.py:128 forms.py:11
|
||||
#: actions.py:219
|
||||
msgid "Selected bills should be in closed state"
|
||||
msgstr "Las facturas seleccionadas están en estado abierto"
|
||||
|
||||
#: actions.py:236
|
||||
#, python-format
|
||||
msgid "%(type)s of %(related_type)s %(number)s and creation date %(date)s"
|
||||
msgstr "%(type)s de %(related_type)s %(number)s con fecha de creación %(date)s"
|
||||
|
||||
#: actions.py:243
|
||||
msgid "%(related_type)s %(number)s subtotal for tax %(tax)s%%"
|
||||
msgstr "%(related_type)s %(number)s subtotal %(tax)s%%"
|
||||
|
||||
#: actions.py:255
|
||||
msgid "<a href=\"%(url)s\">One amendment bill</a> have been generated."
|
||||
msgstr "Se ha creado una <a href=\"%(url)s\">transacción</a>"
|
||||
|
||||
#: actions.py:256
|
||||
msgid "<a href=\"%(url)s\">%(num)i amendment bills</a> have been generated."
|
||||
msgstr "Se han creado <a href=\"%(url)s\">%(num)i transacciones</a>"
|
||||
|
||||
#: actions.py:259
|
||||
msgid "Amend"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:54 admin.py:98 admin.py:133 forms.py:11
|
||||
#: templates/admin/bills/report.html:43
|
||||
msgid "Total"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:80
|
||||
#: admin.py:85
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:88
|
||||
#: admin.py:93
|
||||
msgid "Subtotal"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:118
|
||||
#: admin.py:123
|
||||
msgid "Is open"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:123
|
||||
#: admin.py:128
|
||||
msgid "Subline"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:157
|
||||
#: admin.py:162
|
||||
msgid "No bills selected."
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:164
|
||||
#: admin.py:169
|
||||
#, python-format
|
||||
msgid "Manage %s bill lines."
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:166
|
||||
#: admin.py:171
|
||||
msgid "Bill not in open state."
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:169
|
||||
#: admin.py:174
|
||||
msgid "Not all bills are in open state."
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:170
|
||||
#: admin.py:175
|
||||
msgid "Manage bill lines of multiple bills."
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:190
|
||||
#: admin.py:195
|
||||
msgid "Raw"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:208
|
||||
#: admin.py:214 models.py:72
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:213
|
||||
#: admin.py:220
|
||||
msgid "lines"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:218 templates/bills/microspective.html:118
|
||||
#: admin.py:225 filters.py:44 templates/bills/microspective.html:118
|
||||
msgid "total"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:226 models.py:88 models.py:352
|
||||
#: admin.py:233 models.py:103 models.py:446
|
||||
msgid "type"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:243
|
||||
#: admin.py:250
|
||||
msgid "Payment"
|
||||
msgstr "Pago"
|
||||
|
||||
#: filters.py:17
|
||||
#: filters.py:19
|
||||
msgid "All"
|
||||
msgstr ""
|
||||
|
||||
#: filters.py:18 models.py:78
|
||||
#: filters.py:20 models.py:87
|
||||
msgid "Invoice"
|
||||
msgstr "Factura"
|
||||
|
||||
#: filters.py:19 models.py:79
|
||||
#: filters.py:21 models.py:88
|
||||
msgid "Amendment invoice"
|
||||
msgstr "Factura rectificative"
|
||||
|
||||
#: filters.py:20 models.py:80
|
||||
#: filters.py:22 models.py:89
|
||||
msgid "Fee"
|
||||
msgstr "Quota de socio"
|
||||
|
||||
#: filters.py:21
|
||||
#: filters.py:23
|
||||
msgid "Amendment fee"
|
||||
msgstr "Quota rectificativa"
|
||||
|
||||
#: filters.py:22
|
||||
#: filters.py:24
|
||||
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
|
||||
#: filters.py:66
|
||||
msgid "has bill contact"
|
||||
msgstr ""
|
||||
|
||||
#: forms.py:9
|
||||
#: filters.py:71
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
|
||||
#: filters.py:72
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: filters.py:83
|
||||
msgid "payment state"
|
||||
msgstr "Pago"
|
||||
|
||||
#: filters.py:88 models.py:71
|
||||
msgid "Open"
|
||||
msgstr ""
|
||||
|
||||
#: filters.py:89 models.py:75
|
||||
msgid "Paid"
|
||||
msgstr ""
|
||||
|
||||
#: filters.py:90
|
||||
msgid "Pending"
|
||||
msgstr ""
|
||||
|
||||
#: filters.py:91 models.py:78
|
||||
msgid "Bad debt"
|
||||
msgstr ""
|
||||
|
||||
#: forms.py:9 templates/admin/bills/report.html:37
|
||||
msgid "Number"
|
||||
msgstr ""
|
||||
|
||||
|
@ -217,7 +259,7 @@ msgstr ""
|
|||
msgid "Main"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:23 models.py:86
|
||||
#: models.py:23 models.py:99
|
||||
msgid "account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -249,138 +291,179 @@ msgstr ""
|
|||
msgid "country"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:35
|
||||
#: models.py:35 templates/admin/bills/report.html:38
|
||||
msgid "VAT number"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:67
|
||||
msgid "Paid"
|
||||
#: models.py:73
|
||||
msgid "Processed"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:68
|
||||
msgid "Pending"
|
||||
#: models.py:74
|
||||
msgid "Amended"
|
||||
msgstr "Quota rectificativa"
|
||||
|
||||
#: models.py:76
|
||||
msgid "Incomplete"
|
||||
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"
|
||||
#: models.py:77
|
||||
msgid "Executed"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:90
|
||||
msgid "closed on"
|
||||
msgid "Amendment Fee"
|
||||
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"
|
||||
msgid "Pro forma"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:98
|
||||
msgid "number"
|
||||
msgstr "número"
|
||||
|
||||
#: models.py:101
|
||||
msgid "amend of"
|
||||
msgstr "rectificación de"
|
||||
|
||||
#: models.py:104
|
||||
msgid "created on"
|
||||
msgstr "creado en"
|
||||
|
||||
#: models.py:105
|
||||
msgid "closed on"
|
||||
msgstr "cerrada en"
|
||||
|
||||
#: models.py:106
|
||||
msgid "open"
|
||||
msgstr "abierta"
|
||||
|
||||
#: models.py:107
|
||||
msgid "sent"
|
||||
msgstr "enviada"
|
||||
|
||||
#: models.py:108
|
||||
msgid "due on"
|
||||
msgstr "vencimiento"
|
||||
|
||||
#: models.py:109
|
||||
msgid "updated on"
|
||||
msgstr "actualizada en"
|
||||
|
||||
#: models.py:112
|
||||
msgid "comments"
|
||||
msgstr "comentarios"
|
||||
|
||||
#: models.py:113
|
||||
msgid "HTML"
|
||||
msgstr "HTML"
|
||||
|
||||
#: models.py:192
|
||||
#, python-format
|
||||
msgid "Type %s is not an amendment."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:285
|
||||
#: models.py:194
|
||||
msgid "Amend of related account doesn't match bill account."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:199
|
||||
#, python-format
|
||||
msgid "Type %s requires an amend of link."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:378
|
||||
msgid "bill"
|
||||
msgstr ""
|
||||
msgstr "factura"
|
||||
|
||||
#: models.py:286 models.py:350 templates/bills/microspective.html:73
|
||||
#: models.py:379 models.py:444 templates/bills/microspective.html:73
|
||||
msgid "description"
|
||||
msgstr ""
|
||||
msgstr "descripción"
|
||||
|
||||
#: models.py:287
|
||||
#: models.py:380
|
||||
msgid "rate"
|
||||
msgstr ""
|
||||
msgstr "tarifa"
|
||||
|
||||
#: models.py:288
|
||||
#: models.py:381
|
||||
msgid "quantity"
|
||||
msgstr ""
|
||||
msgstr "cantidad"
|
||||
|
||||
#: models.py:289
|
||||
#: models.py:383
|
||||
msgid "Verbose quantity"
|
||||
msgstr ""
|
||||
msgstr "Cantidad"
|
||||
|
||||
#: models.py:290 templates/bills/microspective.html:77
|
||||
#: models.py:384 templates/bills/microspective.html:77
|
||||
#: templates/bills/microspective.html:111
|
||||
msgid "subtotal"
|
||||
msgstr ""
|
||||
msgstr "subtotal"
|
||||
|
||||
#: models.py:291
|
||||
#: models.py:385
|
||||
msgid "tax"
|
||||
msgstr ""
|
||||
msgstr "impuesto"
|
||||
|
||||
#: models.py:292
|
||||
#: models.py:386
|
||||
msgid "start"
|
||||
msgstr ""
|
||||
msgstr "inicio"
|
||||
|
||||
#: models.py:293
|
||||
#: models.py:387
|
||||
msgid "end"
|
||||
msgstr ""
|
||||
msgstr "fín"
|
||||
|
||||
#: models.py:295
|
||||
#: models.py:389
|
||||
msgid "Informative link back to the order"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:296
|
||||
#: models.py:390
|
||||
msgid "order billed"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:297
|
||||
#: models.py:391
|
||||
msgid "order billed until"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:298
|
||||
#: models.py:392
|
||||
msgid "created"
|
||||
msgstr ""
|
||||
msgstr "creado"
|
||||
|
||||
#: models.py:300
|
||||
#: models.py:394
|
||||
msgid "amended line"
|
||||
msgstr ""
|
||||
msgstr "linea rectificativa"
|
||||
|
||||
#: models.py:343
|
||||
#: models.py:437
|
||||
msgid "Volume"
|
||||
msgstr ""
|
||||
msgstr "Volumen"
|
||||
|
||||
#: models.py:344
|
||||
#: models.py:438
|
||||
msgid "Compensation"
|
||||
msgstr ""
|
||||
msgstr "Compensación"
|
||||
|
||||
#: models.py:345
|
||||
#: models.py:439
|
||||
msgid "Other"
|
||||
msgstr ""
|
||||
msgstr "Otro"
|
||||
|
||||
#: models.py:349
|
||||
#: models.py:443
|
||||
msgid "bill line"
|
||||
msgstr ""
|
||||
msgstr "linea de factura"
|
||||
|
||||
#: templates/admin/bills/report.html:39
|
||||
msgid "Contact"
|
||||
msgstr "Contacto"
|
||||
|
||||
#: templates/admin/bills/report.html:40
|
||||
#, fuzzy
|
||||
#| msgid "Due date"
|
||||
msgid "Close date"
|
||||
msgstr "Fecha de pago"
|
||||
|
||||
#: templates/admin/bills/report.html:41
|
||||
msgid "Base"
|
||||
msgstr "Base"
|
||||
|
||||
#: templates/admin/bills/report.html:42 templates/bills/microspective.html:111
|
||||
#: templates/bills/microspective.html:114
|
||||
msgid "VAT"
|
||||
msgstr "IVA"
|
||||
|
||||
#: templates/bills/microspective-fee.html:107
|
||||
msgid "Due date"
|
||||
|
@ -427,11 +510,6 @@ msgstr "hrs/cant"
|
|||
msgid "rate/price"
|
||||
msgstr "tarifa/precio"
|
||||
|
||||
#: templates/bills/microspective.html:111
|
||||
#: templates/bills/microspective.html:114
|
||||
msgid "VAT"
|
||||
msgstr "IVA"
|
||||
|
||||
#: templates/bills/microspective.html:114
|
||||
msgid "taxes"
|
||||
msgstr "impuestos"
|
||||
|
|
|
@ -90,6 +90,10 @@ class Bill(models.Model):
|
|||
(AMENDMENTFEE, _("Amendment Fee")),
|
||||
(PROFORMA, _("Pro forma")),
|
||||
)
|
||||
AMEND_MAP = {
|
||||
INVOICE: AMENDMENTINVOICE,
|
||||
FEE: AMENDMENTFEE,
|
||||
}
|
||||
|
||||
number = models.CharField(_("number"), max_length=16, unique=True, blank=True)
|
||||
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
|
||||
|
@ -181,6 +185,24 @@ class Bill(models.Model):
|
|||
return self.EXECUTED
|
||||
return self.BAD_DEBT
|
||||
|
||||
def clean(self):
|
||||
if self.amend_of_id:
|
||||
errors = {}
|
||||
if self.type not in self.AMEND_MAP.values():
|
||||
errors['amend_of'] = _("Type %s is not an amendment.") % self.get_type_display()
|
||||
if self.amend_of.account_id != self.account_id:
|
||||
errors['account'] = _("Amend of related account doesn't match bill account.")
|
||||
if self.amend_of.is_open:
|
||||
errors['amend_of'] = _("Related invoice is in open state.")
|
||||
if self.amend_of.type in self.AMEND_MAP.values():
|
||||
errors['amend_of'] = _("Related invoice is an amendment.")
|
||||
if errors:
|
||||
raise ValidationError(errors)
|
||||
elif self.type in self.AMEND_MAP.values():
|
||||
raise ValidationError({
|
||||
'amend_of': _("Type %s requires an amend of link.") % self.get_type_display()
|
||||
})
|
||||
|
||||
def get_total(self):
|
||||
if not self.is_open:
|
||||
return self.total
|
||||
|
@ -201,11 +223,7 @@ class Bill(models.Model):
|
|||
return self.type or self.get_class_type()
|
||||
|
||||
def get_amend_type(self):
|
||||
amend_map = {
|
||||
self.INVOICE: self.AMENDMENTINVOICE,
|
||||
self.FEE: self.AMENDMENTFEE,
|
||||
}
|
||||
amend_type = amend_map.get(self.type)
|
||||
amend_type = self.AMEND_MAP.get(self.type)
|
||||
if amend_type is None:
|
||||
raise TypeError("%s has no associated amend type." % self.type)
|
||||
return amend_type
|
||||
|
@ -321,10 +339,17 @@ class Bill(models.Model):
|
|||
subtotals[tax] = (subtotal, round(tax/100*subtotal, 2))
|
||||
return subtotals
|
||||
|
||||
def compute_base(self):
|
||||
bases = self.lines.annotate(
|
||||
bases=F('subtotal') + Coalesce(F('sublines__total'), 0)
|
||||
)
|
||||
return round(bases.aggregate(Sum('bases'))['bases__sum'] or 0, 2)
|
||||
|
||||
def compute_total(self):
|
||||
totals = self.lines.annotate(
|
||||
totals=(F('subtotal') + Coalesce(F('sublines__total'), 0)) * (1+F('tax')/100))
|
||||
return round(totals.aggregate(Sum('totals'))['totals__sum'], 2)
|
||||
totals=(F('subtotal') + Coalesce(F('sublines__total'), 0)) * (1+F('tax')/100)
|
||||
)
|
||||
return round(totals.aggregate(Sum('totals'))['totals__sum'] or 0, 2)
|
||||
|
||||
|
||||
class Invoice(Bill):
|
||||
|
@ -363,7 +388,7 @@ class BillLine(models.Model):
|
|||
subtotal = models.DecimalField(_("subtotal"), max_digits=12, decimal_places=2)
|
||||
tax = models.DecimalField(_("tax"), max_digits=4, decimal_places=2)
|
||||
start_on = models.DateField(_("start"))
|
||||
end_on = models.DateField(_("end"), null=True)
|
||||
end_on = models.DateField(_("end"), null=True, blank=True)
|
||||
order = models.ForeignKey(settings.BILLS_ORDER_MODEL, null=True, blank=True,
|
||||
help_text=_("Informative link back to the order"), on_delete=models.SET_NULL)
|
||||
order_billed_on = models.DateField(_("order billed"), null=True, blank=True)
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
{% load i18n utils %}
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Bill Report</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<style type="text/css">
|
||||
@page {
|
||||
size: 11.69in 8.27in;
|
||||
}
|
||||
table {
|
||||
font-family: sans;
|
||||
font-size: 10px;
|
||||
max-width: 10in;
|
||||
}
|
||||
table tr:nth-child(even) {
|
||||
background-color: #eee;
|
||||
}
|
||||
table tr:nth-child(odd) {
|
||||
background-color: #fff;
|
||||
}
|
||||
table th {
|
||||
color: white;
|
||||
background-color: grey;
|
||||
}
|
||||
.item.column-base, .item.column-vat, .item.column-total, .item.column-number {
|
||||
text-align: right;
|
||||
}
|
||||
.column-vat-number {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr id="transaction">
|
||||
<th class="title column-number">{% trans "Number" %}</th>
|
||||
<th class="title column-vat-number">{% trans "VAT number" %}</th>
|
||||
<th class="title column-billcontant">{% trans "Contact" %}</th>
|
||||
<th class="title column-date">{% trans "Close date" %}</th>
|
||||
<th class="title column-base">{% trans "Base" %}</th>
|
||||
<th class="title column-vat">{% trans "VAT" %}</th>
|
||||
<th class="title column-total">{% trans "Total" %}</th>
|
||||
</tr>
|
||||
{% for bill in bills %}
|
||||
<tr>
|
||||
<td class="item column-number">{{ bill.number }}</td>
|
||||
<td class="item column-vat-number">{{ bill.buyer.vat }}</td>
|
||||
<td class="item column-billcontant">{{ bill.buyer.get_name }}</td>
|
||||
<td class="item column-date">{{ bill.closed_on|date }}</td>
|
||||
{% with base=bill.compute_base total=bill.compute_total %}
|
||||
<td class="item column-base">{{ base }} &{{ currency }};</td>
|
||||
<td class="item column-vat">{{ total|sub:base }} &{{ currency }};</td>
|
||||
<td class="item column-total">{{ total }} &{{ currency }};</td>
|
||||
{% endwith %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -4,7 +4,7 @@ from django import forms
|
|||
from django.core.exceptions import ValidationError
|
||||
|
||||
from .utils import normurlpath
|
||||
from .validators import validate_domain_protocol
|
||||
from .validators import validate_domain_protocol, validate_server_name
|
||||
|
||||
|
||||
class WebsiteAdminForm(forms.ModelForm):
|
||||
|
@ -15,12 +15,16 @@ class WebsiteAdminForm(forms.ModelForm):
|
|||
if not domains:
|
||||
return self.cleaned_data
|
||||
protocol = self.cleaned_data.get('protocol')
|
||||
for domain in domains.all():
|
||||
domains = domains.all()
|
||||
for domain in domains:
|
||||
try:
|
||||
validate_domain_protocol(self.instance, domain, protocol)
|
||||
except ValidationError as e:
|
||||
# TODO not sure about this one
|
||||
self.add_error(None, e)
|
||||
except ValidationError as err:
|
||||
self.add_error(None, err)
|
||||
try:
|
||||
validate_server_name(domains)
|
||||
except ValidationError as err:
|
||||
self.add_error('domains', err)
|
||||
return self.cleaned_data
|
||||
|
||||
|
||||
|
|
|
@ -28,3 +28,11 @@ def validate_domain_protocol(website, domain, protocol):
|
|||
raise ValidationError({
|
||||
'domains': 'A website is already defined for "%s" on protocol %s' % (domain, protocol),
|
||||
})
|
||||
|
||||
|
||||
def validate_server_name(domains):
|
||||
if domains:
|
||||
for domain in domains:
|
||||
if not domain.name.startswith('*'):
|
||||
return
|
||||
raise ValidationError(_("At least one non-wildcard domain should be provided."))
|
||||
|
|
|
@ -96,3 +96,7 @@ def admin_url(obj):
|
|||
def isactive(obj):
|
||||
return getattr(obj, 'is_active', True)
|
||||
|
||||
|
||||
@register.filter
|
||||
def sub(value, arg):
|
||||
return value - arg
|
||||
|
|
Loading…
Reference in New Issue