Coverage for functions \ flipdare \ firestore \ core \ pledge_event_transaction.py: 49%
39 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-05-08 12:22 +1000
« prev ^ index » next coverage.py v7.13.0, created at 2026-05-08 12:22 +1000
1from __future__ import annotations
3from typing import Any
5from google.cloud.firestore import DocumentReference, Increment
7from flipdare.generated.model.payment.payment_event_model import PaymentEventModel
8from flipdare.generated.model.payment.payment_model import PaymentKeys
9from flipdare.generated.model.payment.payment_result_model import PaymentResultModel
10from flipdare.generated.model.payment.pledge_model import PledgeKeys
11from flipdare.generated.shared.firestore_collections import FirestoreCollections
12from flipdare.generated.shared.payment.payment_status import PaymentStatus
13from flipdare.util.time_util import FirestoreTime
14from flipdare.wrapper.payment.payment_event_wrapper import PaymentEventWrapper
15from flipdare.wrapper.payment.pledge_wrapper import PledgeWrapper
17_PK = PledgeKeys
18_MK = PaymentKeys
19_PLEDGE = FirestoreCollections.PLEDGE.value
20_EVENTS = FirestoreCollections.PLEDGE_PAYMENT_EVENTS.value
23class PledgeEventTransaction:
25 def __init__(self, parent_db: Any) -> None:
26 self.parent_db = parent_db
27 self.client = parent_db.client
29 def add_event(
30 self,
31 pledge: PledgeWrapper,
32 event: PaymentEventModel,
33 payment_status: PaymentStatus | None = None,
34 result: PaymentResultModel | None = None,
35 ) -> PaymentEventWrapper:
36 """
37 Atomically add a payment event and update the pledge in a single batch.
39 Uses dot-notation field paths to update nested pledge.payment fields
40 without rewriting the entire payment object.
41 """
42 pledge_id = pledge.doc_id
43 batch = self.client.batch()
45 # 1. Create event document in subcollection
46 pledge_ref = self.client.collection(_PLEDGE).document(pledge_id)
47 event_ref: DocumentReference = pledge_ref.collection(_EVENTS).document()
48 event_payload = event.to_dict()
49 batch.set(event_ref, event_payload)
51 # 2. update payment_event_count
52 # 3. update last_event
53 # 4. update payment status if provided
54 # 5. update payment result total if provided
55 pledge_updates: dict[str, Any] = {
56 _PK.PAYMENT_EVENT_CT: Increment(1),
57 f"{_PK.PAYMENT}.{_MK.LAST_EVENT}": event_payload,
58 f"{_PK.PAYMENT}.{_MK.UPDATED_AT}": FirestoreTime.server_timestamp(),
59 }
60 if payment_status is not None:
61 pledge_updates[f"{_PK.PAYMENT}.{_MK.STATUS}"] = payment_status
63 if result is not None:
64 if pledge._model.payment is None:
65 raise ValueError("Cannot update payment result when payment is None")
67 existing_result = pledge._model.payment.result_total
68 existing_result.accumulate(result)
70 pledge_updates[f"{_PK.PAYMENT}.{_MK.RESULT_TOTAL}"] = existing_result.to_dict()
72 batch.update(pledge_ref, pledge_updates)
73 batch.commit()
75 return PaymentEventWrapper.from_dict({**event_payload, "id": event_ref.id})