|
|
@@ -1,5 +1,166 @@
|
|
1
|
|
-from django.views.generic import TemplateView
|
|
|
1
|
+import csv
|
|
|
2
|
+
|
|
|
3
|
+from django.views.generic import TemplateView, ListView
|
|
|
4
|
+from django.contrib import messages
|
|
|
5
|
+from django.utils.datastructures import SortedDict
|
|
|
6
|
+from django.template.defaultfilters import date as format_date
|
|
|
7
|
+from django.db.models.loading import get_model
|
|
|
8
|
+from django.http import HttpResponse, HttpResponseRedirect
|
|
|
9
|
+from django.core.urlresolvers import reverse
|
|
|
10
|
+
|
|
|
11
|
+from oscar.apps.dashboard.forms import OrderSearchForm
|
|
|
12
|
+
|
|
|
13
|
+Order = get_model('order', 'Order')
|
|
2
|
14
|
|
|
3
|
15
|
|
|
4
|
16
|
class IndexView(TemplateView):
|
|
5
|
17
|
template_name = 'dashboard/index.html'
|
|
|
18
|
+
|
|
|
19
|
+
|
|
|
20
|
+class OrderListView(ListView):
|
|
|
21
|
+ model = Order
|
|
|
22
|
+ context_object_name = 'orders'
|
|
|
23
|
+ template_name = 'dashboard/orders/order_list.html'
|
|
|
24
|
+ form_class = OrderSearchForm
|
|
|
25
|
+ base_description = 'All orders'
|
|
|
26
|
+ paginate_by = 25
|
|
|
27
|
+ description = ''
|
|
|
28
|
+ actions = ('download_selected_orders',)
|
|
|
29
|
+
|
|
|
30
|
+ def get_queryset(self):
|
|
|
31
|
+ """
|
|
|
32
|
+ Build the queryset for this list and also update the title that
|
|
|
33
|
+ describes the queryset
|
|
|
34
|
+ """
|
|
|
35
|
+ queryset = self.model.objects.all().order_by('-date_placed')
|
|
|
36
|
+ self.description = self.base_description
|
|
|
37
|
+
|
|
|
38
|
+ if 'order_number' not in self.request.GET:
|
|
|
39
|
+ self.form = self.form_class()
|
|
|
40
|
+ return queryset
|
|
|
41
|
+
|
|
|
42
|
+ self.form = self.form_class(self.request.GET)
|
|
|
43
|
+ if not self.form.is_valid():
|
|
|
44
|
+ return queryset
|
|
|
45
|
+
|
|
|
46
|
+ data = self.form.cleaned_data
|
|
|
47
|
+
|
|
|
48
|
+ if data['order_number']:
|
|
|
49
|
+ queryset = self.model.objects.filter(number__istartswith=data['order_number'])
|
|
|
50
|
+ self.description = 'Orders with number starting with "%s"' % data['order_number']
|
|
|
51
|
+
|
|
|
52
|
+ if data['name']:
|
|
|
53
|
+ # If the value is two words, then assume they are first name and last name
|
|
|
54
|
+ parts = data['name'].split()
|
|
|
55
|
+ if len(parts) == 2:
|
|
|
56
|
+ queryset = queryset.filter(Q(user__first_name__istartswith=parts[0]) |
|
|
|
57
|
+ Q(user__last_name__istartswith=parts[1])).distinct()
|
|
|
58
|
+ else:
|
|
|
59
|
+ queryset = queryset.filter(Q(user__first_name__istartswith=data['name']) |
|
|
|
60
|
+ Q(user__last_name__istartswith=data['name'])).distinct()
|
|
|
61
|
+ self.description += " with customer name matching '%s'" % data['name']
|
|
|
62
|
+
|
|
|
63
|
+ if data['product_title']:
|
|
|
64
|
+ queryset = queryset.filter(lines__title__istartswith=data['product_title']).distinct()
|
|
|
65
|
+ self.description += " including an item with title matching '%s'" % data['product_title']
|
|
|
66
|
+
|
|
|
67
|
+ if data['product_id']:
|
|
|
68
|
+ queryset = queryset.filter(Q(lines__upc=data['product_id']) |
|
|
|
69
|
+ Q(lines__product_id=data['product_id'])).distinct()
|
|
|
70
|
+ self.description += " including an item with ID '%s'" % data['product_id']
|
|
|
71
|
+
|
|
|
72
|
+ if data['date_from'] and data['date_to']:
|
|
|
73
|
+ # Add 24 hours to make search inclusive
|
|
|
74
|
+ date_to = data['date_to'] + datetime.timedelta(days=1)
|
|
|
75
|
+ queryset = queryset.filter(date_placed__gte=data['date_from']).filter(date_placed__lt=date_to)
|
|
|
76
|
+ self.description += " placed between %s and %s" % (format_date(data['date_from']), format_date(data['date_to']))
|
|
|
77
|
+ elif data['date_from']:
|
|
|
78
|
+ queryset = queryset.filter(date_placed__gte=data['date_from'])
|
|
|
79
|
+ self.description += " placed since %s" % format_date(data['date_from'])
|
|
|
80
|
+ elif data['date_to']:
|
|
|
81
|
+ date_to = data['date_to'] + datetime.timedelta(days=1)
|
|
|
82
|
+ queryset = queryset.filter(date_placed__lt=date_to)
|
|
|
83
|
+ self.description += " placed before %s" % format_date(data['date_to'])
|
|
|
84
|
+
|
|
|
85
|
+ if data['voucher']:
|
|
|
86
|
+ queryset = queryset.filter(discounts__voucher_code=data['voucher']).distinct()
|
|
|
87
|
+ self.description += " using voucher '%s'" % data['voucher']
|
|
|
88
|
+
|
|
|
89
|
+ if data['payment_method']:
|
|
|
90
|
+ queryset = queryset.filter(sources__source_type__code=data['payment_method']).distinct()
|
|
|
91
|
+ self.description += " paid for by %s" % data['payment_method']
|
|
|
92
|
+
|
|
|
93
|
+ if data['status']:
|
|
|
94
|
+ queryset = queryset.filter(status=data['status'])
|
|
|
95
|
+ self.description += " with status %s" % data['status']
|
|
|
96
|
+
|
|
|
97
|
+ return queryset
|
|
|
98
|
+
|
|
|
99
|
+ def get_context_data(self, **kwargs):
|
|
|
100
|
+ ctx = super(OrderListView, self).get_context_data(**kwargs)
|
|
|
101
|
+ ctx['queryset_description'] = self.description
|
|
|
102
|
+ ctx['form'] = self.form
|
|
|
103
|
+ return ctx
|
|
|
104
|
+
|
|
|
105
|
+ def is_csv_download(self):
|
|
|
106
|
+ return self.request.GET.get('response_format', None) == 'csv'
|
|
|
107
|
+
|
|
|
108
|
+ def get_paginate_by(self, queryset):
|
|
|
109
|
+ return None if self.is_csv_download() else self.paginate_by
|
|
|
110
|
+
|
|
|
111
|
+ def render_to_response(self, context):
|
|
|
112
|
+ if self.is_csv_download():
|
|
|
113
|
+ return self.download_selected_orders(self.request, context['object_list'])
|
|
|
114
|
+ return super(OrderListView, self).render_to_response(context)
|
|
|
115
|
+
|
|
|
116
|
+ def post(self, request, *args, **kwargs):
|
|
|
117
|
+ action = request.POST.get('action', '').lower()
|
|
|
118
|
+ if action not in self.actions:
|
|
|
119
|
+ messages.error(self.request, "Invalid action")
|
|
|
120
|
+ return HttpResponseRedirect(reverse('dashboard:orders'))
|
|
|
121
|
+ order_ids = request.POST.getlist('selected_order')
|
|
|
122
|
+ if not order_ids:
|
|
|
123
|
+ messages.error(self.request, "You need to select some orders")
|
|
|
124
|
+ return HttpResponseRedirect(reverse('dashboard:orders'))
|
|
|
125
|
+
|
|
|
126
|
+ raw_orders = Order.objects.in_bulk(order_ids)
|
|
|
127
|
+ orders = (raw_orders[int(id)] for id in order_ids)
|
|
|
128
|
+ return getattr(self, action)(request, orders)
|
|
|
129
|
+
|
|
|
130
|
+ def download_selected_orders(self, request, orders):
|
|
|
131
|
+ response = HttpResponse(mimetype='text/csv')
|
|
|
132
|
+ response['Content-Disposition'] = 'attachment; filename=orders.csv'
|
|
|
133
|
+ writer = csv.writer(response, delimiter=',')
|
|
|
134
|
+
|
|
|
135
|
+ meta_data = (('number', 'Order number'),
|
|
|
136
|
+ ('value', 'Order value'),
|
|
|
137
|
+ ('date', 'Date of purchase'),
|
|
|
138
|
+ ('num_items', 'Number of items'),
|
|
|
139
|
+ ('status', 'Order status'),
|
|
|
140
|
+ ('shipping_address_name', 'Deliver to name'),
|
|
|
141
|
+ ('billing_address_name', 'Bill to name'),
|
|
|
142
|
+ )
|
|
|
143
|
+ columns = SortedDict()
|
|
|
144
|
+ for k,v in meta_data:
|
|
|
145
|
+ columns[k] = v
|
|
|
146
|
+
|
|
|
147
|
+ writer.writerow(columns.values())
|
|
|
148
|
+ for order in orders:
|
|
|
149
|
+ row = columns.copy()
|
|
|
150
|
+ row['number'] = order.number
|
|
|
151
|
+ row['value'] = order.total_incl_tax
|
|
|
152
|
+ row['date'] = order.date_placed
|
|
|
153
|
+ row['num_items'] = order.num_items
|
|
|
154
|
+ row['status'] = order.status
|
|
|
155
|
+ if order.shipping_address:
|
|
|
156
|
+ row['shipping_address_name'] = order.shipping_address.name()
|
|
|
157
|
+ else:
|
|
|
158
|
+ row['shipping_address_name'] = ''
|
|
|
159
|
+ if order.billing_address:
|
|
|
160
|
+ row['billing_address_name'] = order.billing_address.name()
|
|
|
161
|
+ else:
|
|
|
162
|
+ row['billing_address_name'] = ''
|
|
|
163
|
+
|
|
|
164
|
+ encoded_values = [unicode(value).encode('utf8') for value in row.values()]
|
|
|
165
|
+ writer.writerow(encoded_values)
|
|
|
166
|
+ return response
|