Coverage for api/operations/orders.py: 12%
174 statements
« prev ^ index » next coverage.py v7.6.2, created at 2024-10-10 03:02 +0300
« prev ^ index » next coverage.py v7.6.2, created at 2024-10-10 03:02 +0300
1import datetime
2from typing import Any
4from bson import ObjectId
5from dbcc import MongoTableEngine
6from exceptions import BadParameterHTTPError, NotFoundHTTPError
7from mongodb import (
8 clients_col,
9 docs_col,
10 drivers_col,
11 orders_col,
12 orgs_col,
13 subsidiaries_col,
14 trailers_col,
15 trucks_col,
16 users_col,
17)
18from pymongo.errors import DuplicateKeyError
19from pymongo.results import InsertOneResult
20from services.documents import documents_querier
21from sotrans_models.models._base import InsertByOIDModel
22from sotrans_models.models.misc.client import ClientDBModel
23from sotrans_models.models.misc.document import (
24 DocumentCreateModel,
25 DocumentDBModel,
26 RequestDocumentType,
27)
28from sotrans_models.models.misc.update import UpdateMetadata
29from sotrans_models.models.orders.order import (
30 ConfirmedOrderUpdateModel,
31 OrderCreateModel,
32 OrderDBModel,
33 OrderStatus,
34 OrderUpdateModel,
35)
36from sotrans_models.models.resources.trucks import TruckType
37from sotrans_models.models.users import SotransOIDCUserModel
38from sotrans_models.utils.text_mappers import (
39 get_clients_text_search,
40 get_documents_text_search,
41 update_text_search_for_data,
42)
43from utils.check_carriers_resources import check_verification
44from utils.dt_utils import get_current_datetime
45from utils.helper import (
46 Order,
47 get_hash,
48 place_stop_indicies,
49 update_order_address,
50)
53async def fill_carrier_by_oid_company(
54 order_data: dict, order: OrderCreateModel | OrderUpdateModel
55):
56 if order.carrier and "id" in order.carrier.model_fields_set:
57 if order.carrier.id is None:
58 order_data["carrier"] = None
59 else:
60 carrier = await orgs_col.find_single(
61 "_id", ObjectId(order.carrier.id), projection={"documents": 0}
62 )
63 if not carrier:
64 raise NotFoundHTTPError("перевозчик")
65 order_data["carrier"] = carrier
68async def fill_orders_subsidiary_or_logistician(
69 data: dict[str, Any], order: Order
70):
71 if "assigned" in data:
72 del data["assigned"]
73 if not (order.assigned and order.assigned.company):
74 return
75 if (
76 order.assigned.company.subsidiary
77 and "id" in order.assigned.company.subsidiary.model_fields_set
78 ):
79 data["assigned"] = {"company": {}}
80 if order.assigned.company.subsidiary.id is None:
81 data["assigned"]["company"]["subsidiary"] = None
82 subsidiary = await subsidiaries_col.find_single(
83 "_id", order.assigned.company.subsidiary.id
84 )
85 if subsidiary:
86 data["assigned"]["company"]["subsidiary"] = subsidiary
87 if (
88 order.assigned.company.employee
89 and "id" in order.assigned.company.employee.model_fields_set
90 ):
91 if "assigned" not in data:
92 data["assigned"] = {"company": {}}
93 if order.assigned.company.employee.id is None:
94 data["assigned"]["company"]["employee"] = None
95 company_logistician = await users_col.find_single(
96 "id", order.assigned.company.employee.id
97 )
98 if company_logistician is None:
99 return
100 data["assigned"]["company"]["employee"] = company_logistician
103async def fill_orders_client(
104 data: dict[str, Any],
105 order: OrderCreateModel | OrderUpdateModel,
106 by_external: bool = False,
107):
108 if "client" not in data:
109 return # patch case
110 if "id" in order.client.model_fields_set:
111 if order.client.id is None:
112 raise BadParameterHTTPError("нет клиента")
113 else:
114 client_data = await clients_col.find_single(
115 "_id", ObjectId(data["client"]["id"])
116 )
117 if client_data is None:
118 raise NotFoundHTTPError("клиент")
119 elif (
120 "inn" in data["client"]
121 and data["client"]["inn"]
122 and "kpp" in data["client"]
123 and data["client"]["kpp"]
124 ):
125 client_data = await clients_col.collection.find_one(
126 {
127 ClientDBModel.inn: data["client"]["inn"],
128 ClientDBModel.kpp: data["client"]["kpp"],
129 }
130 )
131 if not client_data:
132 if by_external:
133 raise NotFoundHTTPError("клиент")
134 try:
135 client_data = await clients_col.create(data["client"])
136 except DuplicateKeyError:
137 raise BadParameterHTTPError(
138 "клиент с такими ИНН и КПП уже зарегистрирован"
139 )
140 client_model = ClientDBModel(**client_data)
141 text_search = get_clients_text_search(client_model)
142 await clients_col.update_by_id(
143 client_model.id, {ClientDBModel.text_search: text_search}
144 )
145 else:
146 return
148 data["client"] = client_data
151async def prepare_to_create_order(
152 executor: SotransOIDCUserModel,
153 order: OrderCreateModel,
154 by_external: bool = False,
155) -> dict:
156 place_stop_indicies(order)
158 order_data = order.model_dump()
159 await fill_orders_client(order_data, order, by_external=by_external)
160 await update_order_address(order_data, by_external=by_external)
161 order_data["created_at"] = get_current_datetime()
162 order_data["updated"] = [
163 UpdateMetadata(
164 timestamp=order_data["created_at"],
165 previous_values=None,
166 author=executor.sub,
167 ).model_dump()
168 ]
169 order_data["etag"] = get_hash(order_data)
170 order_data["status"] = OrderStatus.buffer.value
171 return order_data
174async def update_documents_data(
175 order_id: ObjectId,
176 order: ConfirmedOrderUpdateModel | OrderCreateModel | OrderUpdateModel,
177 order_data: dict,
178 carrier_container: dict | bool = False,
179):
180 """
181 :param carrier_container: is used to get existing carrier from order data
182 False if it is not possible,
183 True if needs to be retrieved,
184 order_data if it is queried already
185 """
186 if not order.documents:
187 return
188 order_data["documents"] = []
189 doc_insert_ids = [
190 ObjectId(document.id)
191 for document in order.documents
192 if isinstance(document, InsertByOIDModel) and document.id
193 ]
194 if doc_insert_ids:
195 doc_inserts = await docs_col.find_batch(
196 {"_id": {"$in": doc_insert_ids}}
197 )
198 for doc in doc_inserts:
199 check_verification(
200 doc, f'Document {doc.get("name", "(nameless)")}'
201 )
202 order_data["documents"].extend(doc_inserts)
203 carrier = None
204 if carrier_container is True:
205 carrier_container = await orders_col.find_single("id", order_id)
206 if carrier_container:
207 carrier = carrier_container.get("carrier") # type: ignore[union-attr]
208 for doc in order.documents:
209 if not isinstance(doc, DocumentCreateModel):
210 continue
211 doc_data = doc.model_dump()
212 doc_data["created_at"] = get_current_datetime()
213 doc_data["object_id"] = order_id
214 doc_data["collection"] = "orders"
215 # fixme organization_id of person who loads
216 doc_data[DocumentDBModel.text_search] = get_documents_text_search(
217 DocumentDBModel(**doc_data)
218 )
219 if carrier:
220 doc_data["organization_id"] = carrier.get("id")
221 doc_created = await docs_col.create(
222 DocumentDBModel(**doc_data).model_dump()
223 )
224 order_data["documents"].append(doc_created)
227async def fill_by_insert_oid_resources_docs(
228 order_data: dict,
229 order: ConfirmedOrderUpdateModel | OrderCreateModel,
230 order_id: ObjectId | None = None,
231 with_carrier: bool = False,
232) -> dict:
233 if order.driver and "id" in order.driver.model_fields_set:
234 if order.driver.id is None:
235 order_data[OrderDBModel.driver] = None
236 else:
237 order_data["driver"] = await drivers_col.find_single(
238 "_id", ObjectId(order.driver.id)
239 )
240 check_verification(order_data["driver"], "водитель")
241 if order.truck and "id" in order.truck.model_fields_set:
242 if order.truck.id is None:
243 order_data[OrderDBModel.truck] = None
244 else:
245 order_data["truck"] = await trucks_col.find_single(
246 "_id", ObjectId(order.truck.id)
247 )
248 check_verification(order_data["truck"], "тягач")
249 if order.trailer and "id" in order.trailer.model_fields_set:
250 if order.trailer.id is None:
251 order_data[OrderDBModel.trailer] = None
252 else:
253 order_data["trailer"] = await trailers_col.find_single(
254 "_id", ObjectId(order.trailer.id)
255 )
256 check_verification(order_data["trailer"], "прицеп")
257 if order_id and order.documents:
258 await update_documents_data(order_id, order, order_data, with_carrier)
259 return order_data
262async def patch_operations(update_data: dict, order: OrderUpdateModel):
263 await fill_orders_subsidiary_or_logistician(update_data, order)
264 await fill_orders_client(update_data, order)
265 await update_order_address(update_data, by_external=False)
268async def make_data_update_for_confirmed(
269 up_data: dict, order: OrderUpdateModel | OrderCreateModel
270):
271 await fill_carrier_by_oid_company(up_data, order)
272 await fill_by_insert_oid_resources_docs(up_data, order, with_carrier=False)
273 up_data[OrderDBModel.status] = OrderStatus.confirmed.value
274 up_data[
275 OrderDBModel.confirmation_end_time
276 ] = get_current_datetime() + datetime.timedelta(hours=1)
279async def generate_draft_document(
280 model_to_update: OrderDBModel,
281) -> DocumentDBModel:
282 e = BadParameterHTTPError("указано недостаточно ресурсов")
283 if not (model_to_update.driver and model_to_update.truck):
284 raise e
285 if (
286 model_to_update.truck.truck_type == TruckType.tractor_unit
287 and not model_to_update.trailer
288 ):
289 raise e
290 saved_path = await documents_querier.upload_document(model_to_update)
291 saved_path = saved_path.strip('"')
292 doc_name = saved_path.split("/")[-1]
293 now = get_current_datetime()
294 documents_draft = DocumentDBModel(
295 type=RequestDocumentType.order_request_draft.value,
296 link=saved_path,
297 name=doc_name,
298 created_at=now,
299 organization_id=model_to_update.carrier.id,
300 )
301 return documents_draft
304def pre_process_to_exchange_auction_end_time(
305 order_data: dict[str, Any],
306 order: OrderUpdateModel,
307 update_data: dict[str, Any],
308):
309 auction_end_time = order and order.auction_end_time
310 if not (order_data.get(OrderDBModel.auction_end_time) or auction_end_time):
311 raise BadParameterHTTPError("нет времени завершения аукциона")
313 if auction_end_time:
314 update_data[OrderDBModel.auction_end_time] = auction_end_time
317async def clear_documents(documents: list[dict[str, ObjectId | Any]] | None):
318 if not documents:
319 return
320 cdt = get_current_datetime()
321 await docs_col.collection.update_many(
322 {"id": {"$in": [d.get("id") for d in documents]}},
323 {"$set": {DocumentDBModel.deleted_at: cdt}},
324 )
327async def create_order_with_second_phase(
328 order_data: dict, col: MongoTableEngine, *, move: bool = False
329) -> OrderDBModel:
330 created: InsertOneResult = await col.collection.insert_one(order_data)
332 order_model = OrderDBModel(**order_data)
333 if move is False:
334 short_id = str(created.inserted_id)[-6:].upper()
335 order_model.short_id = short_id
336 order_model.id = created.inserted_id
337 text_search = update_text_search_for_data(order_model)
338 update_data = {OrderDBModel.text_search: text_search}
339 if move is False:
340 update_data |= {
341 OrderDBModel.id: order_model.id,
342 OrderDBModel.short_id: order_model.short_id,
343 }
344 await col.update_by_id(created.inserted_id, update_data)
345 order_model.text_search = text_search
346 return order_model