more creditnote stuff
This commit is contained in:
parent
3e401f03bc
commit
062f7502d0
|
@ -285,6 +285,9 @@ class EpayPayment(CreatedUpdatedModel, UUIDModel):
|
||||||
|
|
||||||
|
|
||||||
class CreditNote(CreatedUpdatedModel):
|
class CreditNote(CreatedUpdatedModel):
|
||||||
|
class Meta:
|
||||||
|
ordering = ['-created']
|
||||||
|
|
||||||
amount = models.DecimalField(max_digits=10, decimal_places=2)
|
amount = models.DecimalField(max_digits=10, decimal_places=2)
|
||||||
text = models.TextField()
|
text = models.TextField()
|
||||||
pdf = models.FileField(
|
pdf = models.FileField(
|
||||||
|
@ -300,7 +303,7 @@ class CreditNote(CreatedUpdatedModel):
|
||||||
)
|
)
|
||||||
paid = models.BooleanField(
|
paid = models.BooleanField(
|
||||||
verbose_name=_('Paid?'),
|
verbose_name=_('Paid?'),
|
||||||
help_text=_('Whether this creditnote has been paid.'),
|
help_text=_('Whether the amount in this creditnote has been paid back to the customer.'),
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
sent_to_customer = models.BooleanField(default=False)
|
sent_to_customer = models.BooleanField(default=False)
|
||||||
|
|
36
shop/templates/creditnote_list.html
Normal file
36
shop/templates/creditnote_list.html
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
{% extends 'shop_base.html' %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% load shop_tags %}
|
||||||
|
|
||||||
|
{% block shop_content %}
|
||||||
|
<h3>Credit Notes</h3>
|
||||||
|
<table class="table table-bordered table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Credit Note ID</th>
|
||||||
|
<th>Text</th>
|
||||||
|
<th class="text-right">Amount</th>
|
||||||
|
<th>Paid?</th>
|
||||||
|
<th>PDF</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for creditnote in creditnotes %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ creditnote.id }}</td>
|
||||||
|
<td>{{ creditnote.text }}</td>
|
||||||
|
<td class="text-right">{{ creditnote.amount|currency }}</td>
|
||||||
|
<td class="text-center">{{ creditnote.paid|truefalseicon }}</td>
|
||||||
|
<td>
|
||||||
|
{% if creditnote.pdf %}
|
||||||
|
{% url 'shop:download_creditnote' pk=creditnote.pk as creditnote_download_url %}
|
||||||
|
{% bootstrap_button "PDF" icon="save-file" href=creditnote_download_url button_class="btn-primary btn-xs" %}
|
||||||
|
{% else %}
|
||||||
|
N/A
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
|
@ -17,6 +17,9 @@
|
||||||
<li class="pull-right"><a href="{% url 'shop:ticket_list' %}">Tickets</a></li>
|
<li class="pull-right"><a href="{% url 'shop:ticket_list' %}">Tickets</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li class="pull-right no-before"><a href="{% url 'shop:order_list' %}">Orders</a></li>
|
<li class="pull-right no-before"><a href="{% url 'shop:order_list' %}">Orders</a></li>
|
||||||
|
{% if user.creditnotes.exists %}
|
||||||
|
<li class="pull-right"><a href="{% url 'shop:creditnote_list' %}">Credit Notes</a></li>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -23,4 +23,7 @@ urlpatterns = [
|
||||||
|
|
||||||
url(r'tickets/$', TicketListView.as_view(), name='ticket_list'),
|
url(r'tickets/$', TicketListView.as_view(), name='ticket_list'),
|
||||||
url(r'tickets/(?P<pk>\b[0-9A-Fa-f]{8}\b(-\b[0-9A-Fa-f]{4}\b){3}-\b[0-9A-Fa-f]{12}\b)$', TicketDetailView.as_view(), name='ticket_detail'),
|
url(r'tickets/(?P<pk>\b[0-9A-Fa-f]{8}\b(-\b[0-9A-Fa-f]{4}\b){3}-\b[0-9A-Fa-f]{12}\b)$', TicketDetailView.as_view(), name='ticket_detail'),
|
||||||
|
|
||||||
|
url(r'creditnotes/$', CreditNoteListView.as_view(), name='creditnote_list'),
|
||||||
|
url(r'creditnotes/(?P<pk>[0-9]+)/pdf/$', DownloadCreditNoteView.as_view(), name='download_creditnote'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -40,11 +40,38 @@ from vendor.coinify_callback import CoinifyCallback
|
||||||
import json, time
|
import json, time
|
||||||
|
|
||||||
|
|
||||||
|
class EnsureCreditNoteHasPDFMixin(SingleObjectMixin):
|
||||||
|
model = CreditNote
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
if not self.get_object().pdf:
|
||||||
|
messages.error(request, "This creditnote has no PDF yet!")
|
||||||
|
return HttpResponseRedirect(reverse_lazy('shop:creditnote_list'))
|
||||||
|
|
||||||
|
return super(EnsureCreditNoteHasPDFMixin, self).dispatch(
|
||||||
|
request, *args, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class EnsureUserOwnsCreditNoteMixin(SingleObjectMixin):
|
||||||
|
model = CreditNote
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
# If the user does not own this creditnote OR is not staff
|
||||||
|
if not request.user.is_staff:
|
||||||
|
if self.get_object().user != request.user:
|
||||||
|
raise Http404("CreditNote not found")
|
||||||
|
|
||||||
|
return super(EnsureUserOwnsCreditNoteMixin, self).dispatch(
|
||||||
|
request, *args, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EnsureUserOwnsOrderMixin(SingleObjectMixin):
|
class EnsureUserOwnsOrderMixin(SingleObjectMixin):
|
||||||
model = Order
|
model = Order
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
# If the user does not own this ticket OR is not staff
|
# If the user does not own this order OR is not staff
|
||||||
if not request.user.is_staff:
|
if not request.user.is_staff:
|
||||||
if self.get_object().user != request.user:
|
if self.get_object().user != request.user:
|
||||||
raise Http404("Order not found")
|
raise Http404("Order not found")
|
||||||
|
@ -319,6 +346,24 @@ class DownloadInvoiceView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsurePa
|
||||||
response.write(self.get_object().invoice.pdf.read())
|
response.write(self.get_object().invoice.pdf.read())
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class CreditNoteListView(LoginRequiredMixin, ListView):
|
||||||
|
model = CreditNote
|
||||||
|
template_name = "creditnote_list.html"
|
||||||
|
context_object_name = 'creditnotes'
|
||||||
|
|
||||||
|
|
||||||
|
class DownloadCreditNoteView(LoginRequiredMixin, EnsureUserOwnsCreditNoteMixin, EnsureCreditNoteHasPDFMixin, SingleObjectMixin, View):
|
||||||
|
model = CreditNote
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
response = HttpResponse(content_type='application/pdf')
|
||||||
|
response['Content-Disposition'] = 'attachment; filename="%s"' % self.get_object().filename
|
||||||
|
response.write(self.get_object().invoice.pdf.read())
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
#################################################################################
|
#################################################################################
|
||||||
|
|
||||||
class EpayFormView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUnpaidOrderMixin, EnsureClosedOrderMixin, EnsureOrderHasProductsMixin, DetailView):
|
class EpayFormView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUnpaidOrderMixin, EnsureClosedOrderMixin, EnsureOrderHasProductsMixin, DetailView):
|
||||||
|
|
Loading…
Reference in a new issue