Coverage for .tox / coverage / lib / python3.11 / site-packages / sideshow / web / views / customers.py: 100%
106 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-15 17:10 -0600
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-15 17:10 -0600
1# -*- coding: utf-8; -*-
2################################################################################
3#
4# Sideshow -- Case/Special Order Tracker
5# Copyright © 2024-2025 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 WuttaEnum
30from sideshow.db.model import LocalCustomer, PendingCustomer
31from sideshow.web.views.shared import PendingMixin
32from sideshow.web.util import make_new_order_batches_grid, make_orders_grid
35class LocalCustomerView(MasterView): # pylint: disable=abstract-method
36 """
37 Master view for
38 :class:`~sideshow.db.model.customers.LocalCustomer`; route prefix
39 is ``local_customers``.
41 Notable URLs provided by this class:
43 * ``/local/customers/``
44 * ``/local/customers/new``
45 * ``/local/customers/XXX``
46 * ``/local/customers/XXX/edit``
47 * ``/local/customers/XXX/delete``
48 """
50 model_class = LocalCustomer
51 model_title = "Local Customer"
52 route_prefix = "local_customers"
53 url_prefix = "/local/customers"
55 labels = {
56 "external_id": "External ID",
57 }
59 # pylint: disable=duplicate-code
60 grid_columns = [
61 "external_id",
62 "full_name",
63 "first_name",
64 "last_name",
65 "phone_number",
66 "email_address",
67 ]
68 # pylint: enable=duplicate-code
70 sort_defaults = "full_name"
72 # pylint: disable=duplicate-code
73 form_fields = [
74 "external_id",
75 "full_name",
76 "first_name",
77 "last_name",
78 "phone_number",
79 "email_address",
80 "orders",
81 "new_order_batches",
82 ]
83 # pylint: enable=duplicate-code
85 def configure_grid(self, grid): # pylint: disable=empty-docstring
86 """ """
87 g = grid
88 super().configure_grid(g)
90 # links
91 g.set_link("full_name")
92 g.set_link("first_name")
93 g.set_link("last_name")
94 g.set_link("phone_number")
95 g.set_link("email_address")
97 def configure_form(self, form): # pylint: disable=empty-docstring
98 """ """
99 f = form
100 super().configure_form(f)
101 customer = f.model_instance
103 # external_id
104 if self.creating:
105 f.remove("external_id")
106 else:
107 f.set_readonly("external_id")
109 # full_name
110 if self.creating or self.editing:
111 f.remove("full_name")
113 # orders
114 if self.creating or self.editing:
115 f.remove("orders")
116 else:
117 f.set_grid("orders", self.make_orders_grid(customer))
119 # new_order_batches
120 if self.creating or self.editing:
121 f.remove("new_order_batches")
122 else:
123 f.set_grid("new_order_batches", self.make_new_order_batches_grid(customer))
125 def make_orders_grid(self, customer):
126 """
127 Make and return the grid for the Orders field.
128 """
129 return make_orders_grid(
130 self.request, route_prefix=self.get_route_prefix(), data=customer.orders
131 )
133 def make_new_order_batches_grid(self, customer):
134 """
135 Make and return the grid for the New Order Batches field.
136 """
137 return make_new_order_batches_grid(
138 self.request,
139 route_prefix=self.get_route_prefix(),
140 data=customer.new_order_batches,
141 )
143 def objectify(self, form): # pylint: disable=empty-docstring
144 """ """
145 customer = super().objectify(form)
147 customer.full_name = self.app.make_full_name(
148 customer.first_name, customer.last_name
149 )
151 return customer
154class PendingCustomerView(PendingMixin, MasterView): # pylint: disable=abstract-method
155 """
156 Master view for
157 :class:`~sideshow.db.model.customers.PendingCustomer`; route
158 prefix is ``pending_customers``.
160 Notable URLs provided by this class:
162 * ``/pending/customers/``
163 * ``/pending/customers/new``
164 * ``/pending/customers/XXX``
165 * ``/pending/customers/XXX/edit``
166 * ``/pending/customers/XXX/delete``
167 """
169 model_class = PendingCustomer
170 model_title = "Pending Customer"
171 route_prefix = "pending_customers"
172 url_prefix = "/pending/customers"
174 labels = {
175 "customer_id": "Customer ID",
176 }
178 # pylint: disable=duplicate-code
179 grid_columns = [
180 "full_name",
181 "first_name",
182 "last_name",
183 "phone_number",
184 "email_address",
185 "customer_id",
186 "status",
187 "created",
188 "created_by",
189 ]
190 # pylint: enable=duplicate-code
192 sort_defaults = "full_name"
194 # pylint: disable=duplicate-code
195 form_fields = [
196 "customer_id",
197 "full_name",
198 "first_name",
199 "last_name",
200 "phone_number",
201 "email_address",
202 "status",
203 "created",
204 "created_by",
205 "orders",
206 "new_order_batches",
207 ]
208 # pylint: enable=duplicate-code
210 def configure_grid(self, grid): # pylint: disable=empty-docstring
211 """ """
212 g = grid
213 super().configure_grid(g)
214 enum = self.app.enum
216 # status
217 g.set_renderer("status", self.grid_render_enum, enum=enum.PendingCustomerStatus)
219 # links
220 g.set_link("full_name")
221 g.set_link("first_name")
222 g.set_link("last_name")
223 g.set_link("phone_number")
224 g.set_link("email_address")
226 def configure_form(self, form): # pylint: disable=empty-docstring
227 """ """
228 f = form
229 super().configure_form(f)
230 enum = self.app.enum
232 self.configure_form_pending(f)
234 # customer_id
235 if self.creating:
236 f.remove("customer_id")
237 else:
238 f.set_readonly("customer_id")
240 # status
241 if self.creating:
242 f.remove("status")
243 else:
244 f.set_node("status", WuttaEnum(self.request, enum.PendingCustomerStatus))
245 f.set_readonly("status")
247 def make_orders_grid(self, customer):
248 """
249 Make and return the grid for the Orders field.
250 """
251 return make_orders_grid(
252 self.request, route_prefix=self.get_route_prefix(), data=customer.orders
253 )
255 def make_new_order_batches_grid(self, customer):
256 """
257 Make and return the grid for the New Order Batches field.
258 """
259 return make_new_order_batches_grid(
260 self.request,
261 route_prefix=self.get_route_prefix(),
262 data=customer.new_order_batches,
263 )
265 def objectify(self, form): # pylint: disable=empty-docstring
266 """ """
267 enum = self.app.enum
268 customer = super().objectify(form)
270 if self.creating:
271 customer.status = enum.PendingCustomerStatus.PENDING
272 customer.created_by = self.request.user
274 return customer
276 def delete_instance(self, obj): # pylint: disable=empty-docstring
277 """ """
278 customer = obj
279 model_title = self.get_model_title()
281 # avoid deleting if still referenced by order(s)
282 if list(customer.orders):
283 self.request.session.flash(
284 f"Cannot delete {model_title} still attached to Order(s)", "warning"
285 )
286 raise self.redirect(self.get_action_url("view", customer))
288 # avoid deleting if still referenced by new order batch(es)
289 for batch in customer.new_order_batches:
290 if not batch.executed:
291 self.request.session.flash(
292 f"Cannot delete {model_title} still attached "
293 "to New Order Batch(es)",
294 "warning",
295 )
296 raise self.redirect(self.get_action_url("view", customer))
298 # go ahead and delete per usual
299 super().delete_instance(customer)
302def defaults(config, **kwargs): # pylint: disable=missing-function-docstring
303 base = globals()
305 LocalCustomerView = kwargs.get( # pylint: disable=redefined-outer-name,invalid-name
306 "LocalCustomerView", base["LocalCustomerView"]
307 )
308 LocalCustomerView.defaults(config)
310 PendingCustomerView = ( # pylint: disable=redefined-outer-name,invalid-name
311 kwargs.get("PendingCustomerView", base["PendingCustomerView"])
312 )
313 PendingCustomerView.defaults(config)
316def includeme(config): # pylint: disable=missing-function-docstring
317 defaults(config)