Coverage for .tox/coverage/lib/python3.11/site-packages/sideshow/web/views/customers.py: 100%

143 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-01-09 12:58 -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""" 

26 

27from wuttaweb.views import MasterView 

28from wuttaweb.forms.schema import UserRef, WuttaEnum 

29 

30from sideshow.db.model import LocalCustomer, PendingCustomer 

31 

32 

33class LocalCustomerView(MasterView): 

34 """ 

35 Master view for 

36 :class:`~sideshow.db.model.customers.LocalCustomer`; route prefix 

37 is ``local_customers``. 

38 

39 Notable URLs provided by this class: 

40 

41 * ``/local/customers/`` 

42 * ``/local/customers/new`` 

43 * ``/local/customers/XXX`` 

44 * ``/local/customers/XXX/edit`` 

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

46 """ 

47 model_class = LocalCustomer 

48 model_title = "Local Customer" 

49 route_prefix = 'local_customers' 

50 url_prefix = '/local/customers' 

51 

52 labels = { 

53 'external_id': "External ID", 

54 } 

55 

56 grid_columns = [ 

57 'external_id', 

58 'full_name', 

59 'first_name', 

60 'last_name', 

61 'phone_number', 

62 'email_address', 

63 ] 

64 

65 sort_defaults = 'full_name' 

66 

67 form_fields = [ 

68 'external_id', 

69 'full_name', 

70 'first_name', 

71 'last_name', 

72 'phone_number', 

73 'email_address', 

74 'orders', 

75 'new_order_batches', 

76 ] 

77 

78 def configure_grid(self, g): 

79 """ """ 

80 super().configure_grid(g) 

81 

82 # links 

83 g.set_link('full_name') 

84 g.set_link('first_name') 

85 g.set_link('last_name') 

86 g.set_link('phone_number') 

87 g.set_link('email_address') 

88 

89 def configure_form(self, f): 

90 """ """ 

91 super().configure_form(f) 

92 customer = f.model_instance 

93 

94 # external_id 

95 if self.creating: 

96 f.remove('external_id') 

97 else: 

98 f.set_readonly('external_id') 

99 

100 # full_name 

101 if self.creating or self.editing: 

102 f.remove('full_name') 

103 

104 # orders 

105 if self.creating or self.editing: 

106 f.remove('orders') 

107 else: 

108 f.set_grid('orders', self.make_orders_grid(customer)) 

109 

110 # new_order_batches 

111 if self.creating or self.editing: 

112 f.remove('new_order_batches') 

113 else: 

114 f.set_grid('new_order_batches', self.make_new_order_batches_grid(customer)) 

115 

116 def make_orders_grid(self, customer): 

117 """ 

118 Make and return the grid for the Orders field. 

119 """ 

120 model = self.app.model 

121 route_prefix = self.get_route_prefix() 

122 

123 grid = self.make_grid(key=f'{route_prefix}.view.orders', 

124 model_class=model.Order, 

125 data=customer.orders, 

126 columns=[ 

127 'order_id', 

128 'total_price', 

129 'created', 

130 'created_by', 

131 ], 

132 labels={ 

133 'order_id': "Order ID", 

134 }) 

135 grid.set_renderer('total_price', grid.render_currency) 

136 

137 if self.request.has_perm('orders.view'): 

138 url = lambda order, i: self.request.route_url('orders.view', uuid=order.uuid) 

139 grid.add_action('view', icon='eye', url=url) 

140 grid.set_link('order_id') 

141 

142 return grid 

143 

144 def make_new_order_batches_grid(self, customer): 

145 """ 

146 Make and return the grid for the New Order Batches field. 

147 """ 

148 model = self.app.model 

149 route_prefix = self.get_route_prefix() 

150 

151 grid = self.make_grid(key=f'{route_prefix}.view.new_order_batches', 

152 model_class=model.NewOrderBatch, 

153 data=customer.new_order_batches, 

154 columns=[ 

155 'id', 

156 'total_price', 

157 'created', 

158 'created_by', 

159 'executed', 

160 ], 

161 labels={ 

162 'id': "Batch ID", 

163 }, 

164 renderers={ 

165 'id': 'batch_id', 

166 'total_price': 'currency', 

167 }) 

168 

169 if self.request.has_perm('neworder_batches.view'): 

170 url = lambda batch, i: self.request.route_url('neworder_batches.view', uuid=batch.uuid) 

171 grid.add_action('view', icon='eye', url=url) 

172 grid.set_link('id') 

173 

174 return grid 

175 

176 def objectify(self, form): 

177 """ """ 

178 enum = self.app.enum 

179 customer = super().objectify(form) 

180 

181 customer.full_name = self.app.make_full_name(customer.first_name, 

182 customer.last_name) 

183 

184 return customer 

185 

186 

187class PendingCustomerView(MasterView): 

188 """ 

189 Master view for 

190 :class:`~sideshow.db.model.customers.PendingCustomer`; route 

191 prefix is ``pending_customers``. 

192 

193 Notable URLs provided by this class: 

194 

195 * ``/pending/customers/`` 

196 * ``/pending/customers/new`` 

197 * ``/pending/customers/XXX`` 

198 * ``/pending/customers/XXX/edit`` 

199 * ``/pending/customers/XXX/delete`` 

200 """ 

201 model_class = PendingCustomer 

202 model_title = "Pending Customer" 

203 route_prefix = 'pending_customers' 

204 url_prefix = '/pending/customers' 

205 

206 labels = { 

207 'customer_id': "Customer ID", 

208 } 

209 

210 grid_columns = [ 

211 'full_name', 

212 'first_name', 

213 'last_name', 

214 'phone_number', 

215 'email_address', 

216 'customer_id', 

217 'status', 

218 'created', 

219 'created_by', 

220 ] 

221 

222 sort_defaults = 'full_name' 

223 

224 form_fields = [ 

225 'customer_id', 

226 'full_name', 

227 'first_name', 

228 'last_name', 

229 'phone_number', 

230 'email_address', 

231 'status', 

232 'created', 

233 'created_by', 

234 'orders', 

235 'new_order_batches', 

236 ] 

237 

238 def configure_grid(self, g): 

239 """ """ 

240 super().configure_grid(g) 

241 enum = self.app.enum 

242 

243 # status 

244 g.set_renderer('status', self.grid_render_enum, enum=enum.PendingCustomerStatus) 

245 

246 # links 

247 g.set_link('full_name') 

248 g.set_link('first_name') 

249 g.set_link('last_name') 

250 g.set_link('phone_number') 

251 g.set_link('email_address') 

252 

253 def configure_form(self, f): 

254 """ """ 

255 super().configure_form(f) 

256 enum = self.app.enum 

257 customer = f.model_instance 

258 

259 # customer_id 

260 if self.creating: 

261 f.remove('customer_id') 

262 else: 

263 f.set_readonly('customer_id') 

264 

265 # status 

266 if self.creating: 

267 f.remove('status') 

268 else: 

269 f.set_node('status', WuttaEnum(self.request, enum.PendingCustomerStatus)) 

270 f.set_readonly('status') 

271 

272 # created 

273 if self.creating: 

274 f.remove('created') 

275 else: 

276 f.set_readonly('created') 

277 

278 # created_by 

279 if self.creating: 

280 f.remove('created_by') 

281 else: 

282 f.set_node('created_by', UserRef(self.request)) 

283 f.set_readonly('created_by') 

284 

285 # orders 

286 if self.creating or self.editing: 

287 f.remove('orders') 

288 else: 

289 f.set_grid('orders', self.make_orders_grid(customer)) 

290 

291 # new_order_batches 

292 if self.creating or self.editing: 

293 f.remove('new_order_batches') 

294 else: 

295 f.set_grid('new_order_batches', self.make_new_order_batches_grid(customer)) 

296 

297 def make_orders_grid(self, customer): 

298 """ 

299 Make and return the grid for the Orders field. 

300 """ 

301 model = self.app.model 

302 route_prefix = self.get_route_prefix() 

303 

304 grid = self.make_grid(key=f'{route_prefix}.view.orders', 

305 model_class=model.Order, 

306 data=customer.orders, 

307 columns=[ 

308 'order_id', 

309 'total_price', 

310 'created', 

311 'created_by', 

312 ], 

313 labels={ 

314 'order_id': "Order ID", 

315 }) 

316 grid.set_renderer('total_price', grid.render_currency) 

317 

318 if self.request.has_perm('orders.view'): 

319 url = lambda order, i: self.request.route_url('orders.view', uuid=order.uuid) 

320 grid.add_action('view', icon='eye', url=url) 

321 grid.set_link('order_id') 

322 

323 return grid 

324 

325 def make_new_order_batches_grid(self, customer): 

326 """ 

327 Make and return the grid for the New Order Batches field. 

328 """ 

329 model = self.app.model 

330 route_prefix = self.get_route_prefix() 

331 

332 grid = self.make_grid(key=f'{route_prefix}.view.new_order_batches', 

333 model_class=model.NewOrderBatch, 

334 data=customer.new_order_batches, 

335 columns=[ 

336 'id', 

337 'total_price', 

338 'created', 

339 'created_by', 

340 'executed', 

341 ], 

342 labels={ 

343 'id': "Batch ID", 

344 }, 

345 renderers={ 

346 'id': 'batch_id', 

347 'total_price': 'currency', 

348 }) 

349 

350 if self.request.has_perm('neworder_batches.view'): 

351 url = lambda batch, i: self.request.route_url('neworder_batches.view', uuid=batch.uuid) 

352 grid.add_action('view', icon='eye', url=url) 

353 grid.set_link('id') 

354 

355 return grid 

356 

357 def objectify(self, form): 

358 """ """ 

359 enum = self.app.enum 

360 customer = super().objectify(form) 

361 

362 if self.creating: 

363 customer.status = enum.PendingCustomerStatus.PENDING 

364 customer.created_by = self.request.user 

365 

366 return customer 

367 

368 def delete_instance(self, customer): 

369 """ """ 

370 model_title = self.get_model_title() 

371 

372 # avoid deleting if still referenced by order(s) 

373 for order in customer.orders: 

374 self.request.session.flash(f"Cannot delete {model_title} still attached " 

375 "to Order(s)", 'warning') 

376 raise self.redirect(self.get_action_url('view', customer)) 

377 

378 # avoid deleting if still referenced by new order batch(es) 

379 for batch in customer.new_order_batches: 

380 if not batch.executed: 

381 self.request.session.flash(f"Cannot delete {model_title} still attached " 

382 "to New Order Batch(es)", 'warning') 

383 raise self.redirect(self.get_action_url('view', customer)) 

384 

385 # go ahead and delete per usual 

386 super().delete_instance(customer) 

387 

388 

389def defaults(config, **kwargs): 

390 base = globals() 

391 

392 LocalCustomerView = kwargs.get('LocalCustomerView', base['LocalCustomerView']) 

393 LocalCustomerView.defaults(config) 

394 

395 PendingCustomerView = kwargs.get('PendingCustomerView', base['PendingCustomerView']) 

396 PendingCustomerView.defaults(config) 

397 

398 

399def includeme(config): 

400 defaults(config)