Coverage for src/sideshow/web/views/customers.py: 100%
87 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-06 15:40 -0600
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-06 15:40 -0600
1# -*- coding: utf-8; -*-
2################################################################################
3#
4# Sideshow -- Case/Special Order Tracker
5# Copyright © 2024 Lance Edgar
6#
7# This file is part of Sideshow.
8#
9# Sideshow is free software: you can redistribute it and/or modify it
10# under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# Sideshow is distributed in the hope that it will be useful, but
15# WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17# General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with Sideshow. If not, see <http://www.gnu.org/licenses/>.
21#
22################################################################################
23"""
24Views for Customers
25"""
27from wuttaweb.views import MasterView
28from wuttaweb.forms.schema import UserRef, WuttaEnum
30from sideshow.db.model import PendingCustomer
33class PendingCustomerView(MasterView):
34 """
35 Master view for
36 :class:`~sideshow.db.model.customers.PendingCustomer`; route
37 prefix is ``pending_customers``.
39 Notable URLs provided by this class:
41 * ``/pending/customers/``
42 * ``/pending/customers/new``
43 * ``/pending/customers/XXX``
44 * ``/pending/customers/XXX/edit``
45 * ``/pending/customers/XXX/delete``
46 """
47 model_class = PendingCustomer
48 model_title = "Pending Customer"
49 route_prefix = 'pending_customers'
50 url_prefix = '/pending/customers'
52 labels = {
53 'customer_id': "Customer ID",
54 }
56 grid_columns = [
57 'full_name',
58 'first_name',
59 'last_name',
60 'phone_number',
61 'email_address',
62 'customer_id',
63 'status',
64 'created',
65 'created_by',
66 ]
68 sort_defaults = 'full_name'
70 form_fields = [
71 'customer_id',
72 'full_name',
73 'first_name',
74 'middle_name',
75 'last_name',
76 'phone_number',
77 'phone_type',
78 'email_address',
79 'email_type',
80 'status',
81 'created',
82 'created_by',
83 'orders',
84 'new_order_batches',
85 ]
87 def configure_grid(self, g):
88 """ """
89 super().configure_grid(g)
90 enum = self.app.enum
92 # status
93 g.set_renderer('status', self.grid_render_enum, enum=enum.PendingCustomerStatus)
95 # links
96 g.set_link('full_name')
97 g.set_link('first_name')
98 g.set_link('last_name')
99 g.set_link('phone_number')
100 g.set_link('email_address')
102 def configure_form(self, f):
103 """ """
104 super().configure_form(f)
105 enum = self.app.enum
106 customer = f.model_instance
108 # customer_id
109 if self.creating:
110 f.remove('customer_id')
111 else:
112 f.set_readonly('customer_id')
114 # status
115 if self.creating:
116 f.remove('status')
117 else:
118 f.set_node('status', WuttaEnum(self.request, enum.PendingCustomerStatus))
119 f.set_readonly('status')
121 # created
122 if self.creating:
123 f.remove('created')
124 else:
125 f.set_readonly('created')
127 # created_by
128 if self.creating:
129 f.remove('created_by')
130 else:
131 f.set_node('created_by', UserRef(self.request))
132 f.set_readonly('created_by')
134 # orders
135 if self.creating or self.editing:
136 f.remove('orders')
137 else:
138 f.set_grid('orders', self.make_orders_grid(customer))
140 # new_order_batches
141 if self.creating or self.editing:
142 f.remove('new_order_batches')
143 else:
144 f.set_grid('new_order_batches', self.make_new_order_batches_grid(customer))
146 def make_orders_grid(self, customer):
147 """
148 Make and return the grid for the Orders field.
149 """
150 model = self.app.model
151 route_prefix = self.get_route_prefix()
153 grid = self.make_grid(key=f'{route_prefix}.view.orders',
154 model_class=model.Order,
155 data=customer.orders,
156 columns=[
157 'order_id',
158 'total_price',
159 'created',
160 'created_by',
161 ],
162 labels={
163 'order_id': "Order ID",
164 })
165 grid.set_renderer('total_price', grid.render_currency)
167 if self.request.has_perm('orders.view'):
168 url = lambda order, i: self.request.route_url('orders.view', uuid=order.uuid)
169 grid.add_action('view', icon='eye', url=url)
170 grid.set_link('order_id')
172 return grid
174 def make_new_order_batches_grid(self, customer):
175 """
176 Make and return the grid for the New Order Batches field.
177 """
178 model = self.app.model
179 route_prefix = self.get_route_prefix()
181 grid = self.make_grid(key=f'{route_prefix}.view.new_order_batches',
182 model_class=model.NewOrderBatch,
183 data=customer.new_order_batches,
184 columns=[
185 'id',
186 'total_price',
187 'created',
188 'created_by',
189 'executed',
190 ],
191 labels={
192 'id': "Batch ID",
193 },
194 renderers={
195 'id': 'batch_id',
196 'total_price': 'currency',
197 })
199 if self.request.has_perm('neworder_batches.view'):
200 url = lambda batch, i: self.request.route_url('neworder_batches.view', uuid=batch.uuid)
201 grid.add_action('view', icon='eye', url=url)
202 grid.set_link('id')
204 return grid
206 def objectify(self, form):
207 """ """
208 enum = self.app.enum
209 customer = super().objectify(form)
211 if self.creating:
212 customer.status = enum.PendingCustomerStatus.PENDING
213 customer.created_by = self.request.user
215 return customer
217 def delete_instance(self, customer):
218 """ """
219 model_title = self.get_model_title()
221 # avoid deleting if still referenced by order(s)
222 for order in customer.orders:
223 self.request.session.flash(f"Cannot delete {model_title} still attached "
224 "to Order(s)", 'warning')
225 raise self.redirect(self.get_action_url('view', customer))
227 # avoid deleting if still referenced by new order batch(es)
228 for batch in customer.new_order_batches:
229 if not batch.executed:
230 self.request.session.flash(f"Cannot delete {model_title} still attached "
231 "to New Order Batch(es)", 'warning')
232 raise self.redirect(self.get_action_url('view', customer))
234 # go ahead and delete per usual
235 super().delete_instance(customer)
238def defaults(config, **kwargs):
239 base = globals()
241 PendingCustomerView = kwargs.get('PendingCustomerView', base['PendingCustomerView'])
242 PendingCustomerView.defaults(config)
245def includeme(config):
246 defaults(config)