Coverage for api/handlers/orders/buffer_orders.py: 16%

143 statements  

« prev     ^ index     » next       coverage.py v7.6.2, created at 2024-10-10 03:02 +0300

1import asyncio 

2import datetime 

3from typing import Any 

4 

5from bson import ObjectId 

6from database.entity import update_etag_and_text_search 

7from database.orders import move_order_and_return 

8from exceptions import ( 

9 BadParameterHTTPError, 

10 NoAccessHTTPError, 

11 NotFoundHTTPError, 

12) 

13from handlers.authorization.company_employee_access import ( 

14 assert_client_assigned, 

15 flexible_company_assertion_query, 

16) 

17from handlers.grabbers.orders import buffer_orders_grabber 

18from handlers.misc.clients import on_create_client 

19from mongodb import buf_col, orders_col, trash_orders_col 

20from operations.assignment import data_from_assigned, notify_if_assigned 

21from operations.orders import ( 

22 create_order_with_second_phase, 

23 fill_orders_subsidiary_or_logistician, 

24 make_data_update_for_confirmed, 

25 patch_operations, 

26 pre_process_to_exchange_auction_end_time, 

27 prepare_to_create_order, 

28) 

29from pymongo import ReturnDocument 

30from services.notifications.director import notification_api 

31from services.recommendations import suggestion_api 

32from sotrans_models.models._base import InsertByUUIDModel 

33from sotrans_models.models.misc.client import ClientCreateModel 

34from sotrans_models.models.orders.order import ( 

35 OrderCreateModel, 

36 OrderDBModel, 

37 OrderStatus, 

38 OrderUpdateModel, 

39) 

40from sotrans_models.models.responses import GenericGetListResponse 

41from sotrans_models.models.users import SotransOIDCUserModel 

42from sotrans_models.utils.text_mappers import get_orders_text_search 

43from utils.data_grabber import BaseGetListQueryParams, BaseGetOneQueryParams 

44from utils.helper import ( 

45 add_prices_to_update, 

46 check_assigned, 

47 clean_empty_objects, 

48 etag_detalizer, 

49 place_stop_indicies, 

50) 

51 

52 

53async def on_get_buffer( 

54 executor: SotransOIDCUserModel, params: BaseGetListQueryParams 

55) -> GenericGetListResponse[OrderDBModel]: 

56 return await buffer_orders_grabber.get_list( 

57 params, executor, clients_assignment_default=True 

58 ) 

59 

60 

61async def on_get_one_from_buffer( 

62 executor: SotransOIDCUserModel, 

63 order_id: ObjectId, 

64 params: BaseGetOneQueryParams, 

65): 

66 restriction_q = await flexible_company_assertion_query(executor) 

67 return await buffer_orders_grabber.get_one_by_id_with_pattern( 

68 order_id, params, restriction_q 

69 ) 

70 

71 

72async def on_put_to_exchange( 

73 order_id: ObjectId, executor: SotransOIDCUserModel, order: OrderUpdateModel 

74): 

75 if not order.start_price: 

76 raise BadParameterHTTPError("нет начальной цены") 

77 restriction_q = await flexible_company_assertion_query(executor) 

78 order_data = await buf_col.collection.find_one( 

79 {"id": order_id} | restriction_q 

80 ) 

81 if not order_data: 

82 raise NotFoundHTTPError("заказ") 

83 update_data = await data_from_assigned(order) 

84 pre_process_to_exchange_auction_end_time(order_data, order, update_data) 

85 add_prices_to_update( 

86 order_data, update_data, order.start_price, order.end_price 

87 ) 

88 update_data[OrderDBModel.start_price] = order.start_price 

89 updated_order = await move_order_and_return( 

90 order_id, 

91 buf_col, 

92 orders_col, 

93 OrderStatus.exchange, 

94 restrictions_query=restriction_q, 

95 updates=update_data, 

96 ) 

97 asyncio.create_task( 

98 suggestion_api.create_target_and_recommendation_order( 

99 OrderDBModel(**updated_order) 

100 ) 

101 ) 

102 notify_if_assigned(order, updated_order) 

103 return order 

104 

105 

106def check_create_params( 

107 order: OrderCreateModel | OrderUpdateModel, assigned: bool 

108): 

109 if order.carrier and order.carrier.id: 

110 if not assigned: 

111 raise BadParameterHTTPError("нет ответственных") 

112 if order.start_price: 

113 raise BadParameterHTTPError("лишняя стартовая цена") 

114 elif order.start_price: 

115 if not assigned: 

116 raise BadParameterHTTPError("нет ответственных") 

117 

118 

119async def on_create_order( 

120 executor: SotransOIDCUserModel, order: OrderCreateModel 

121) -> dict[str, Any] | OrderDBModel: 

122 assigned = check_assigned(order) 

123 check_create_params(order, assigned) 

124 if order.start_price and not order.auction_end_time: 

125 raise BadParameterHTTPError("нет времени завершения аукциона") 

126 if isinstance(order.client, ClientCreateModel): 

127 im_responsible = InsertByUUIDModel(id=executor.sub) 

128 if not order.client.responsible: 

129 order.client.responsible = [im_responsible] 

130 elif im_responsible not in order.client.responsible: 

131 order.client.responsible.append(im_responsible) 

132 await on_create_client(order.client) 

133 else: 

134 if not await assert_client_assigned(executor, order.client.id): 

135 raise NoAccessHTTPError("клиент") 

136 

137 order_data = await prepare_to_create_order(executor, order) 

138 if order.end_price or order.start_price: 

139 add_prices_to_update( 

140 order_data, order_data, order.start_price, order.end_price 

141 ) 

142 if order.carrier and order.carrier.id: 

143 await make_data_update_for_confirmed(order_data, order) 

144 elif order.start_price: 

145 order_data[OrderDBModel.status] = OrderStatus.exchange.value 

146 # to appointment if no carrier 

147 elif assigned: 

148 order_data[OrderDBModel.status] = OrderStatus.appointment.value 

149 await fill_orders_subsidiary_or_logistician(order_data, order) 

150 if order.carrier and order.carrier.id or order.start_price or assigned: 

151 created_model = await create_order_with_second_phase( 

152 order_data, orders_col 

153 ) 

154 if order.carrier and order.carrier.id: 

155 asyncio.create_task( 

156 suggestion_api.create_target_and_recommendation_order( 

157 created_model 

158 ) 

159 ) 

160 asyncio.create_task( 

161 notification_api.order_confirmed(created_model) 

162 ) 

163 elif order.start_price: 

164 asyncio.create_task( 

165 suggestion_api.create_target_and_recommendation_order( 

166 created_model, target=False 

167 ) 

168 ) 

169 notification_api.assignment(created_model) 

170 return created_model 

171 buffer_created_model = await create_order_with_second_phase( 

172 order_data, buf_col 

173 ) 

174 asyncio.create_task(notification_api.new_in_buffer(buffer_created_model)) 

175 return buffer_created_model 

176 

177 

178async def on_put_to_trash( 

179 order_id: ObjectId, 

180 executor: SotransOIDCUserModel, 

181): 

182 restriction_q = await flexible_company_assertion_query(executor) 

183 return await move_order_and_return( 

184 order_id, 

185 buf_col, 

186 trash_orders_col, 

187 OrderStatus.archived, 

188 restriction_q, 

189 {OrderDBModel.deleted_at: datetime.datetime.utcnow()}, 

190 ) 

191 

192 

193async def on_update_order( 

194 executor: SotransOIDCUserModel, order_id: ObjectId, order: OrderUpdateModel 

195) -> OrderDBModel | dict[str, Any]: 

196 assigned = check_assigned(order) 

197 check_create_params(order, assigned) 

198 restriction_q = await flexible_company_assertion_query(executor) 

199 etag_q = {"etag": order.etag} if order.etag else {} 

200 order_data = await buf_col.collection.find_one( 

201 {"id": order_id} | restriction_q | etag_q 

202 ) 

203 if order_data is None: 

204 raise NotFoundHTTPError("заказ") 

205 place_stop_indicies(order) 

206 up_data = order.model_dump(exclude_unset=True) 

207 add_prices_to_update( 

208 order_data, up_data, order.start_price, order.end_price 

209 ) 

210 clean_empty_objects(up_data) 

211 await patch_operations(update_data=up_data, order=order) 

212 if assigned: 

213 if order.carrier and order.carrier.id: 

214 await make_data_update_for_confirmed(up_data, order) 

215 order = await move_order_and_return( 

216 order_id, 

217 buf_col, 

218 orders_col, 

219 OrderStatus.confirmed, 

220 restriction_q, 

221 up_data, 

222 ) 

223 om = OrderDBModel(**order) 

224 asyncio.create_task(notification_api.order_confirmed(om)) 

225 notification_api.assignment(om) 

226 return order 

227 if order.start_price: 

228 pre_process_to_exchange_auction_end_time( 

229 order_data, order, up_data 

230 ) 

231 order = await move_order_and_return( 

232 order_id, 

233 buf_col, 

234 orders_col, 

235 OrderStatus.exchange, 

236 restriction_q, 

237 up_data, 

238 ) 

239 notification_api.assignment(OrderDBModel(**order)) 

240 return order 

241 appointed = await move_order_and_return( 

242 order_id, 

243 buf_col, 

244 orders_col, 

245 OrderStatus.appointment, 

246 restriction_q, 

247 up_data, 

248 ) 

249 notification_api.assignment(OrderDBModel(**appointed)) 

250 return appointed 

251 order = await buf_col.collection.find_one_and_update( 

252 restriction_q | {"id": order_id} | etag_q, 

253 {"$set": up_data}, 

254 return_document=ReturnDocument.AFTER, 

255 ) 

256 if not order: 

257 await etag_detalizer(buf_col, etag_q, restriction_q | {"id": order_id}) 

258 raise NotFoundHTTPError("заказ") 

259 await update_etag_and_text_search( 

260 order, buf_col, OrderDBModel, get_orders_text_search 

261 ) 

262 return order 

263 

264 

265async def on_assign_subsidiary( 

266 order_id: ObjectId, 

267 user: SotransOIDCUserModel, 

268 order: OrderUpdateModel, 

269) -> OrderDBModel | dict[str, Any]: 

270 assigned = order.assigned 

271 if not ( 

272 assigned and (assigned.company.subsidiary or assigned.company.employee) 

273 ): 

274 raise BadParameterHTTPError("нет назначения") 

275 update_data = await data_from_assigned(order) 

276 end_price = order.end_price 

277 if end_price is not None: 

278 update_data |= {OrderDBModel.end_price: end_price} 

279 add_prices_to_update( 

280 update_data, 

281 update_data, 

282 order.start_price, 

283 end_price, 

284 ) 

285 restriction_q = await flexible_company_assertion_query(user) 

286 

287 assigned = await move_order_and_return( 

288 order_id, 

289 buf_col, 

290 orders_col, 

291 OrderStatus.appointment, 

292 restrictions_query={OrderDBModel.status: OrderStatus.buffer.value} 

293 | restriction_q, 

294 updates=update_data, 

295 ) 

296 if not assigned: 

297 raise NotFoundHTTPError("заказ") 

298 ao = OrderDBModel(**assigned) 

299 notification_api.assignment(ao) 

300 return ao