this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Move to Account level locks in billing

+50 -11
+15 -10
care/emr/api/viewsets/invoice.py
··· 17 17 EMRRetrieveMixin, 18 18 EMRUpdateMixin, 19 19 ) 20 - from care.emr.locks.billing import InvoiceCreateLock, InvoiceLock 20 + from care.emr.locks.billing import ( 21 + AccountLock, 22 + ChargeItemsLock, 23 + InvoiceCreateLock, 24 + InvoiceLock, 25 + ) 21 26 from care.emr.models.account import Account 22 27 from care.emr.models.charge_item import ChargeItem 23 28 from care.emr.models.invoice import Invoice ··· 129 134 def perform_create(self, instance): 130 135 instance.status = InvoiceStatusOptions.draft.value 131 136 instance.facility = self.get_facility_obj() 132 - with transaction.atomic(): 137 + with transaction.atomic() , AccountLock(instance.account): 133 138 charge_items = ChargeItem.objects.filter( 134 139 account=instance.account, 135 140 status=ChargeItemStatusOptions.billable.value, ··· 230 235 @action(methods=["POST"], detail=True) 231 236 def attach_items_to_invoice(self, request, *args, **kwargs): 232 237 invoice = self.get_object() 233 - with InvoiceLock(invoice): 238 + with AccountLock(invoice.account): 234 239 self.authorize_update({}, invoice) 235 240 self.check_invoice_in_draft(invoice) 236 241 request_params = AttachChargeItemToInvoiceRequest(**request.data) ··· 240 245 account=invoice.account, 241 246 status=ChargeItemStatusOptions.billable.value, 242 247 ) 243 - invoice.charge_items = invoice.charge_items + list( 244 - charge_items.values_list("id", flat=True) 245 - ) 248 + extra_charge_items = list(charge_items.values_list("id", flat=True)) 249 + invoice.charge_items = invoice.charge_items + extra_charge_items 246 250 sync_invoice_items(invoice) 247 251 invoice.updated_by = self.request.user 248 252 invoice.save() 249 253 charge_items.update( 250 - status=ChargeItemStatusOptions.billed.value, paid_invoice=invoice 254 + status=ChargeItemStatusOptions.billed.value, 255 + paid_invoice=invoice, 251 256 ) 252 257 return Response(InvoiceRetrieveSpec.serialize(invoice).to_json()) 253 258 ··· 257 262 @action(methods=["POST"], detail=True) 258 263 def remove_item_from_invoice(self, request, *args, **kwargs): 259 264 invoice = self.get_object() 260 - with InvoiceLock(invoice): 265 + with AccountLock(invoice.account): 261 266 self.authorize_update({}, invoice) 262 267 self.check_invoice_in_draft(invoice) 263 268 request_params = RemoveChargeItemFromInvoiceRequest(**request.data) ··· 284 289 @action(methods=["POST"], detail=True) 285 290 def attach_account_to_invoice(self, request, *args, **kwargs): 286 291 invoice = self.get_object() 287 - with InvoiceLock(invoice): 292 + with AccountLock(invoice.account): 288 293 self.authorize_update({}, invoice) 289 294 self.check_invoice_in_draft(invoice) 290 295 with transaction.atomic(): ··· 304 309 @action(methods=["POST"], detail=True) 305 310 def cancel_invoice(self, request, *args, **kwargs): 306 311 invoice = self.get_object() 307 - with InvoiceLock(invoice): 312 + with AccountLock(invoice.account): 308 313 if invoice.created_date >= care_now() - timedelta( 309 314 minutes=settings.INVOICE_FREE_CANCEL_PERIOD_MINUTES 310 315 ):
+6 -1
care/emr/locks/billing.py
··· 1 1 from django.conf import settings 2 2 3 - from care.utils.lock import Lock 3 + from care.utils.lock import Lock, MultipleItemsLock 4 4 5 5 6 6 class AccountLock(Lock): ··· 31 31 def __init__(self, charge_item, timeout=settings.LOCK_TIMEOUT): 32 32 self.key = f"lock:charge_item:{charge_item.id}" 33 33 self.timeout = timeout 34 + 35 + 36 + class ChargeItemsLock(MultipleItemsLock): 37 + def get_key(self, key): 38 + return f"lock:charge_item:{key}" 34 39 35 40 36 41 class InventoryItemLock(Lock):
+29
care/utils/lock.py
··· 28 28 def __exit__(self, exc_type, exc_value, traceback): 29 29 self.release() 30 30 return False 31 + 32 + 33 + class MultipleItemsLock: 34 + def get_key(self, key): 35 + return f"lock:{key}" 36 + 37 + def __init__(self, keys, timeout=settings.LOCK_TIMEOUT): 38 + self.keys = [self.get_key(key) for key in keys] 39 + self.aquired_keys = [] 40 + self.timeout = timeout 41 + 42 + def acquire(self): 43 + for key in self.keys: 44 + if not cache.set(key, value=True, timeout=self.timeout, nx=True): 45 + self.release() 46 + raise ObjectLocked 47 + self.aquired_keys.append(key) 48 + 49 + def release(self): 50 + for key in self.aquired_keys: 51 + cache.delete(key) 52 + 53 + def __enter__(self): 54 + self.acquire() 55 + return self 56 + 57 + def __exit__(self, exc_type, exc_value, traceback): 58 + self.release() 59 + return False