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

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""" 

26 

27from wuttaweb.views import MasterView 

28from wuttaweb.forms.schema import WuttaEnum 

29 

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 

33 

34 

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``. 

40 

41 Notable URLs provided by this class: 

42 

43 * ``/local/customers/`` 

44 * ``/local/customers/new`` 

45 * ``/local/customers/XXX`` 

46 * ``/local/customers/XXX/edit`` 

47 * ``/local/customers/XXX/delete`` 

48 """ 

49 

50 model_class = LocalCustomer 

51 model_title = "Local Customer" 

52 route_prefix = "local_customers" 

53 url_prefix = "/local/customers" 

54 

55 labels = { 

56 "external_id": "External ID", 

57 } 

58 

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 

69 

70 sort_defaults = "full_name" 

71 

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 

84 

85 def configure_grid(self, grid): # pylint: disable=empty-docstring 

86 """ """ 

87 g = grid 

88 super().configure_grid(g) 

89 

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") 

96 

97 def configure_form(self, form): # pylint: disable=empty-docstring 

98 """ """ 

99 f = form 

100 super().configure_form(f) 

101 customer = f.model_instance 

102 

103 # external_id 

104 if self.creating: 

105 f.remove("external_id") 

106 else: 

107 f.set_readonly("external_id") 

108 

109 # full_name 

110 if self.creating or self.editing: 

111 f.remove("full_name") 

112 

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)) 

118 

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)) 

124 

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 ) 

132 

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 ) 

142 

143 def objectify(self, form): # pylint: disable=empty-docstring 

144 """ """ 

145 customer = super().objectify(form) 

146 

147 customer.full_name = self.app.make_full_name( 

148 customer.first_name, customer.last_name 

149 ) 

150 

151 return customer 

152 

153 

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``. 

159 

160 Notable URLs provided by this class: 

161 

162 * ``/pending/customers/`` 

163 * ``/pending/customers/new`` 

164 * ``/pending/customers/XXX`` 

165 * ``/pending/customers/XXX/edit`` 

166 * ``/pending/customers/XXX/delete`` 

167 """ 

168 

169 model_class = PendingCustomer 

170 model_title = "Pending Customer" 

171 route_prefix = "pending_customers" 

172 url_prefix = "/pending/customers" 

173 

174 labels = { 

175 "customer_id": "Customer ID", 

176 } 

177 

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 

191 

192 sort_defaults = "full_name" 

193 

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 

209 

210 def configure_grid(self, grid): # pylint: disable=empty-docstring 

211 """ """ 

212 g = grid 

213 super().configure_grid(g) 

214 enum = self.app.enum 

215 

216 # status 

217 g.set_renderer("status", self.grid_render_enum, enum=enum.PendingCustomerStatus) 

218 

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") 

225 

226 def configure_form(self, form): # pylint: disable=empty-docstring 

227 """ """ 

228 f = form 

229 super().configure_form(f) 

230 enum = self.app.enum 

231 

232 self.configure_form_pending(f) 

233 

234 # customer_id 

235 if self.creating: 

236 f.remove("customer_id") 

237 else: 

238 f.set_readonly("customer_id") 

239 

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") 

246 

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 ) 

254 

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 ) 

264 

265 def objectify(self, form): # pylint: disable=empty-docstring 

266 """ """ 

267 enum = self.app.enum 

268 customer = super().objectify(form) 

269 

270 if self.creating: 

271 customer.status = enum.PendingCustomerStatus.PENDING 

272 customer.created_by = self.request.user 

273 

274 return customer 

275 

276 def delete_instance(self, obj): # pylint: disable=empty-docstring 

277 """ """ 

278 customer = obj 

279 model_title = self.get_model_title() 

280 

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)) 

287 

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)) 

297 

298 # go ahead and delete per usual 

299 super().delete_instance(customer) 

300 

301 

302def defaults(config, **kwargs): # pylint: disable=missing-function-docstring 

303 base = globals() 

304 

305 LocalCustomerView = kwargs.get( # pylint: disable=redefined-outer-name,invalid-name 

306 "LocalCustomerView", base["LocalCustomerView"] 

307 ) 

308 LocalCustomerView.defaults(config) 

309 

310 PendingCustomerView = ( # pylint: disable=redefined-outer-name,invalid-name 

311 kwargs.get("PendingCustomerView", base["PendingCustomerView"]) 

312 ) 

313 PendingCustomerView.defaults(config) 

314 

315 

316def includeme(config): # pylint: disable=missing-function-docstring 

317 defaults(config)