From ee736de4a4288e1afa967dc47db70ee74b4d2d6f Mon Sep 17 00:00:00 2001 From: erikvw Date: Wed, 27 Nov 2024 21:23:24 -0600 Subject: [PATCH] restrict perms/codenames between site and central pharmacist --- .../admin/actions/process_repack_request.py | 10 +-- edc_pharmacy/auth_objects.py | 76 +++++++++++++------ ...receiveitem_task_id_receiveitem_task_id.py | 23 ++++++ edc_pharmacy/models/signals.py | 23 +++--- edc_pharmacy/models/stock/receive_item.py | 2 + edc_pharmacy/tasks/__init__.py | 1 - edc_pharmacy/tasks/process_repack_request.py | 24 ------ edc_pharmacy/utils/__init__.py | 23 ++++++ edc_pharmacy/utils/allocate_stock.py | 3 + edc_pharmacy/utils/blinded_user.py | 3 + edc_pharmacy/utils/confirm_stock.py | 3 + edc_pharmacy/utils/confirm_stock_at_site.py | 3 + .../utils/create_new_stock_on_receive.py | 21 +++++ edc_pharmacy/utils/dispense.py | 3 + edc_pharmacy/utils/format_qty.py | 3 + edc_pharmacy/utils/get_codenames.py | 27 +++++++ edc_pharmacy/utils/get_random_code.py | 3 + edc_pharmacy/utils/miscellaneous.py | 47 +----------- edc_pharmacy/utils/process_repack_request.py | 18 +++-- .../utils/process_repack_request_queryset.py | 23 ++++++ edc_pharmacy/utils/stock_request/__init__.py | 2 + .../bulk_create_stock_request_items.py | 3 + .../remove_subjects_where_stock_on_site.py | 3 + edc_pharmacy/utils/transfer_stock.py | 6 +- .../update_previous_refill_end_datetime.py | 3 + 25 files changed, 234 insertions(+), 122 deletions(-) create mode 100644 edc_pharmacy/migrations/0054_historicalreceiveitem_task_id_receiveitem_task_id.py delete mode 100644 edc_pharmacy/tasks/__init__.py delete mode 100644 edc_pharmacy/tasks/process_repack_request.py create mode 100644 edc_pharmacy/utils/create_new_stock_on_receive.py create mode 100644 edc_pharmacy/utils/get_codenames.py create mode 100644 edc_pharmacy/utils/process_repack_request_queryset.py diff --git a/edc_pharmacy/admin/actions/process_repack_request.py b/edc_pharmacy/admin/actions/process_repack_request.py index f563799..d338550 100644 --- a/edc_pharmacy/admin/actions/process_repack_request.py +++ b/edc_pharmacy/admin/actions/process_repack_request.py @@ -4,9 +4,9 @@ from django.http import HttpResponseRedirect from django.urls import reverse from django.utils.html import format_html -from edc_utils.celery import celery_is_active, run_task_sync_or_async +from edc_utils.celery import run_task_sync_or_async -from ...tasks.process_repack_request import process_repack_request_queryset +from ...utils import process_repack_request_queryset @admin.action(description="Process repack request") @@ -20,12 +20,6 @@ def process_repack_request_action(modeladmin, request, queryset): """ repack_request_pks = [obj.pk for obj in queryset] - - # if celery is not running, just keep the first pk - if not celery_is_active(): - repack_request_pks = repack_request_pks[:1] - - # run task / func and update or clear the task_id task = run_task_sync_or_async( process_repack_request_queryset, repack_request_pks=repack_request_pks, diff --git a/edc_pharmacy/auth_objects.py b/edc_pharmacy/auth_objects.py index 5528eb9..ddcf18b 100644 --- a/edc_pharmacy/auth_objects.py +++ b/edc_pharmacy/auth_objects.py @@ -1,6 +1,5 @@ -from django.apps import apps as django_apps +from edc_pharmacy.utils import get_codenames -app_name = "edc_pharmacy" # groups DISPENSING = "DISPENSING" DISPENSING_VIEW = "DISPENSING_VIEW" @@ -16,30 +15,64 @@ PHARMACY_PRESCRIBER_ROLE = "PHARMACY_PRESCRIBER_ROLE" SITE_PHARMACIST_ROLE = "SITE_PHARMACIST_ROLE" -pharmacy_codenames = ["edc_pharmacy.view_subject"] -prescriber_codenames = ["edc_pharmacy.view_subject"] - - -navbar_codenames = [ - "edc_pharmacy.nav_pharmacy_section", -] +navbar_codenames = ["edc_pharmacy.nav_pharmacy_section"] navbar_tuples = [] for codename in navbar_codenames: navbar_tuples.append((codename, f"Can access {codename.split('.')[1]}")) -for app_config in django_apps.get_app_configs(): - if app_config.name in [ - "edc_pharmacy", - ]: - for model_cls in app_config.get_models(): - app_name, model_name = model_cls._meta.label_lower.split(".") - if model_name == "registeredsubjectproxy": - continue - for prefix in ["view_", "add_", "change_", "delete_"]: - pharmacy_codenames.append(f"{app_name}.{prefix}{model_name}") +# central pharmacist +view_only_models = [ + "edc_pharmacy.allocation", + "edc_pharmacy.formulation", + "edc_pharmacy.formulationtype", + "edc_pharmacy.frequencyunits", + "edc_pharmacy.location", + "edc_pharmacy.medication", + "edc_pharmacy.stockrequest", + "edc_pharmacy.stockrequestitem", + "edc_pharmacy.stock", + "edc_pharmacy.assignment", + "edc_pharmacy.subject", + "edc_pharmacy.visitschedule", +] +pharmacy_codenames = get_codenames([], view_only_models=view_only_models) +pharmacy_codenames.extend(navbar_codenames) +pharmacy_codenames.sort() + + +# site pharmacist +exclude_models = ["edc_pharmacy.lot", "edc_pharmacy.assignment"] +view_only_models = [ + "edc_pharmacy.order", + "edc_pharmacy.orderitem", + "edc_pharmacy.receive", + "edc_pharmacy.receiveitem", + "edc_pharmacy.repackrequest", + "edc_pharmacy.stocktransfer", + "edc_pharmacy.stocktransferitem", + "edc_pharmacy.allocation", + "edc_pharmacy.container", + "edc_pharmacy.containertype", + "edc_pharmacy.formulation", + "edc_pharmacy.formulationtype", + "edc_pharmacy.frequencyunits", + "edc_pharmacy.location", + "edc_pharmacy.medication", + "edc_pharmacy.product", + "edc_pharmacy.stock", + "edc_pharmacy.subject", + "edc_pharmacy.visitschedule", +] +pharmacy_site_codenames = get_codenames( + [], view_only_models=view_only_models, exclude_models=exclude_models +) +pharmacy_site_codenames.extend(navbar_codenames) +pharmacy_site_codenames.sort() +# prescriber +prescriber_codenames = [] for model_name in ["dosageguideline", "formulation", "medication", "rxrefill"]: prescriber_codenames.extend( [ @@ -50,8 +83,5 @@ ) for model_name in ["rx", "rxitem"]: prescriber_codenames.extend([c for c in pharmacy_codenames if model_name in c]) - -pharmacy_codenames.extend(navbar_codenames) +prescriber_codenames.append("edc_pharmacy.view_subject") prescriber_codenames.sort() -pharmacy_codenames.sort() -pharmacy_site_codenames = [c for c in pharmacy_codenames if not c.endswith("lot")] diff --git a/edc_pharmacy/migrations/0054_historicalreceiveitem_task_id_receiveitem_task_id.py b/edc_pharmacy/migrations/0054_historicalreceiveitem_task_id_receiveitem_task_id.py new file mode 100644 index 0000000..2b1bd52 --- /dev/null +++ b/edc_pharmacy/migrations/0054_historicalreceiveitem_task_id_receiveitem_task_id.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.2 on 2024-11-27 16:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("edc_pharmacy", "0053_alter_location_managers_alter_historicalstock_lot_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="historicalreceiveitem", + name="task_id", + field=models.UUIDField(null=True), + ), + migrations.AddField( + model_name="receiveitem", + name="task_id", + field=models.UUIDField(null=True), + ), + ] diff --git a/edc_pharmacy/models/signals.py b/edc_pharmacy/models/signals.py index 34f8d11..3fc95a2 100644 --- a/edc_pharmacy/models/signals.py +++ b/edc_pharmacy/models/signals.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from decimal import Decimal from celery.states import PENDING @@ -9,8 +11,11 @@ from ..exceptions import InsufficientStockError from ..model_mixins import StudyMedicationCrfModelMixin -from ..tasks import process_repack_request -from ..utils import update_previous_refill_end_datetime +from ..utils import ( + create_new_stock_on_receive, + process_repack_request, + update_previous_refill_end_datetime, +) from .stock import ( Allocation, DispenseItem, @@ -98,19 +103,9 @@ def receive_item_on_post_save(sender, instance, raw, created, update_fields, **k if unit_qty_received == unit_qty: order.status = COMPLETE order.save() + # add to stock - for i in range(0, int(instance.qty)): - Stock.objects.create( - receive_item_id=instance.id, - qty_in=1, - qty_out=0, - qty=1, - product_id=instance.order_item.product.id, - container_id=instance.container.id, - location_id=instance.receive.location.id, - confirmed=False, - lot_id=instance.lot.id, - ) + create_new_stock_on_receive(receive_item_pk=instance.id) @receiver(post_save, sender=StockRequest, dispatch_uid="stock_request_on_post_save") diff --git a/edc_pharmacy/models/stock/receive_item.py b/edc_pharmacy/models/stock/receive_item.py index 1249f7b..970d09c 100644 --- a/edc_pharmacy/models/stock/receive_item.py +++ b/edc_pharmacy/models/stock/receive_item.py @@ -65,6 +65,8 @@ class ReceiveItem(BaseUuidModel): help_text="Quantity x Container.Quantity, e.g. 10 x Bottle of 128 = 1280", ) + task_id = models.UUIDField(null=True) + objects = Manager() history = HistoricalRecords() diff --git a/edc_pharmacy/tasks/__init__.py b/edc_pharmacy/tasks/__init__.py deleted file mode 100644 index 1c8241f..0000000 --- a/edc_pharmacy/tasks/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .process_repack_request import process_repack_request diff --git a/edc_pharmacy/tasks/process_repack_request.py b/edc_pharmacy/tasks/process_repack_request.py deleted file mode 100644 index 73cf7e3..0000000 --- a/edc_pharmacy/tasks/process_repack_request.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import annotations - -from uuid import UUID - -from celery import current_app, shared_task - -from ..utils import process_repack_request as process_repack_request_util - - -@shared_task -def process_repack_request(repack_request_id: UUID, username: str = None): - process_repack_request_util(repack_request_id=repack_request_id, username=username) - return None - - -@shared_task -def process_repack_request_queryset(repack_request_pks: list[UUID], username: str = None): - i = current_app.control.inspect() - active_workers = i.active() - if not active_workers: - repack_request_pks = repack_request_pks[:1] - for pk in repack_request_pks: - process_repack_request_util(repack_request_id=pk, username=username) - return None diff --git a/edc_pharmacy/utils/__init__.py b/edc_pharmacy/utils/__init__.py index bd85f9f..84d955f 100644 --- a/edc_pharmacy/utils/__init__.py +++ b/edc_pharmacy/utils/__init__.py @@ -2,14 +2,37 @@ from .blinded_user import blinded_user from .confirm_stock import confirm_stock from .confirm_stock_at_site import confirm_stock_at_site +from .create_new_stock_on_receive import create_new_stock_on_receive from .dispense import dispense from .format_qty import format_qty +from .get_codenames import get_codenames from .get_random_code import get_random_code from .miscellaneous import get_rx_model_cls, get_rxrefill_model_cls from .process_repack_request import process_repack_request +from .process_repack_request_queryset import process_repack_request_queryset from .stock_request import ( bulk_create_stock_request_items, remove_subjects_where_stock_on_site, ) from .transfer_stock import transfer_stock from .update_previous_refill_end_datetime import update_previous_refill_end_datetime + +__all__ = [ + "allocate_stock", + "blinded_user", + "bulk_create_stock_request_items", + "confirm_stock", + "confirm_stock_at_site", + "create_new_stock_on_receive", + "dispense", + "format_qty", + "get_codenames", + "get_random_code", + "get_rx_model_cls", + "get_rxrefill_model_cls", + "process_repack_request", + "process_repack_request_queryset", + "remove_subjects_where_stock_on_site", + "transfer_stock", + "update_previous_refill_end_datetime", +] diff --git a/edc_pharmacy/utils/allocate_stock.py b/edc_pharmacy/utils/allocate_stock.py index f303e24..46708fa 100644 --- a/edc_pharmacy/utils/allocate_stock.py +++ b/edc_pharmacy/utils/allocate_stock.py @@ -68,3 +68,6 @@ def allocate_stock( obj.save() allocated += 1 return allocated, unallocated + + +__all__ = ["allocate_stock"] diff --git a/edc_pharmacy/utils/blinded_user.py b/edc_pharmacy/utils/blinded_user.py index ea9c3b1..b941480 100644 --- a/edc_pharmacy/utils/blinded_user.py +++ b/edc_pharmacy/utils/blinded_user.py @@ -11,3 +11,6 @@ def blinded_user(request) -> bool: return True # user is not blinded return False + + +__all__ = ["blinded_user"] diff --git a/edc_pharmacy/utils/confirm_stock.py b/edc_pharmacy/utils/confirm_stock.py index d39d9ea..efb9042 100644 --- a/edc_pharmacy/utils/confirm_stock.py +++ b/edc_pharmacy/utils/confirm_stock.py @@ -62,3 +62,6 @@ def confirm_stock( else: already_confirmed.append(stock.code) return confirmed, already_confirmed, invalid + + +__all__ = ["confirm_stock"] diff --git a/edc_pharmacy/utils/confirm_stock_at_site.py b/edc_pharmacy/utils/confirm_stock_at_site.py index c622138..e55315b 100644 --- a/edc_pharmacy/utils/confirm_stock_at_site.py +++ b/edc_pharmacy/utils/confirm_stock_at_site.py @@ -74,3 +74,6 @@ def confirm_stock_at_site( obj.save() confirmed.append(stock_code) return confirmed, already_confirmed, invalid + + +__all__ = ["confirm_stock_at_site"] diff --git a/edc_pharmacy/utils/create_new_stock_on_receive.py b/edc_pharmacy/utils/create_new_stock_on_receive.py new file mode 100644 index 0000000..b548277 --- /dev/null +++ b/edc_pharmacy/utils/create_new_stock_on_receive.py @@ -0,0 +1,21 @@ +from uuid import UUID + +from django.apps import apps as django_apps + + +def create_new_stock_on_receive(receive_item_pk: UUID = None): + receive_item_model_cls = django_apps.get_model("edc_pharmacy.receiveitem") + stock_model_cls = django_apps.get_model("edc_pharmacy.stock") + receive_item = receive_item_model_cls.objects.get(pk=receive_item_pk) + for i in range(0, int(receive_item.qty)): + stock_model_cls.objects.create( + receive_item_id=receive_item.id, + qty_in=1, + qty_out=0, + qty=1, + product_id=receive_item.order_item.product.id, + container_id=receive_item.container.id, + location_id=receive_item.receive.location.id, + confirmed=False, + lot_id=receive_item.lot.id, + ) diff --git a/edc_pharmacy/utils/dispense.py b/edc_pharmacy/utils/dispense.py index c71a322..e5ef9ab 100644 --- a/edc_pharmacy/utils/dispense.py +++ b/edc_pharmacy/utils/dispense.py @@ -63,3 +63,6 @@ def dispense( return dispense_item_model_cls.objects.filter(dispense=dispense_obj) return None + + +__all__ = ["dispense"] diff --git a/edc_pharmacy/utils/format_qty.py b/edc_pharmacy/utils/format_qty.py index 14424a0..24a5961 100644 --- a/edc_pharmacy/utils/format_qty.py +++ b/edc_pharmacy/utils/format_qty.py @@ -14,3 +14,6 @@ def format_qty(qty: Decimal, container: Container): elif container.qty_decimal_places == 1: return "{:0.1f}".format(qty) return "{:0.2f}".format(qty) + + +__all__ = ["format_qty"] diff --git a/edc_pharmacy/utils/get_codenames.py b/edc_pharmacy/utils/get_codenames.py new file mode 100644 index 0000000..eca6012 --- /dev/null +++ b/edc_pharmacy/utils/get_codenames.py @@ -0,0 +1,27 @@ +from django.apps import apps as django_apps + + +def get_codenames( + codenames: list[str], + view_only_models: list[str] | None = None, + exclude_models: list[str] | None = None, +) -> list[str]: + """Return a list of all codenames for the role. + + See auths.py + """ + view_only_models = view_only_models or [] + exclude_models = exclude_models or [] + for app_config in django_apps.get_app_configs(): + if app_config.name in ["edc_pharmacy"]: + for model_cls in app_config.get_models(): + label_lower = model_cls._meta.label_lower + app_name, model_name = label_lower.split(".") + if label_lower in exclude_models: + continue + elif label_lower in view_only_models: + codenames.append(f"{app_name}.view_{model_name}") + else: + for prefix in ["view_", "add_", "change_", "delete_"]: + codenames.append(f"{app_name}.{prefix}{model_name}") + return codenames diff --git a/edc_pharmacy/utils/get_random_code.py b/edc_pharmacy/utils/get_random_code.py index fc0643b..0ca8cab 100644 --- a/edc_pharmacy/utils/get_random_code.py +++ b/edc_pharmacy/utils/get_random_code.py @@ -22,3 +22,6 @@ def get_random_code(model_cls, length: int, tries: int | None = None) -> str: if x == tries: raise StopIteration() return random_code + + +__all__ = ["get_random_code"] diff --git a/edc_pharmacy/utils/miscellaneous.py b/edc_pharmacy/utils/miscellaneous.py index 92a977e..7a4c09e 100644 --- a/edc_pharmacy/utils/miscellaneous.py +++ b/edc_pharmacy/utils/miscellaneous.py @@ -11,49 +11,4 @@ def get_rx_model_cls(): return django_apps.get_model("edc_pharmacy.rx") -# def get_and_check_stock(stock_identifier): -# stock_model_cls = django_apps.get_model("edc_pharmacy.stock") -# stock = stock_model_cls.objects.get(stock_identifier=stock_identifier) -# if not stock.confirmed: -# raise StockError(f"Stock item is not confirmed. Unable to process. Got {stock}.") -# if stock.unit_qty_in - stock.unit_qty_out == Decimal(0): -# raise InsufficientStockError( -# f"Not in stock. Cannot repack. Got stock {stock.stock_identifier}." -# ) -# if stock.unit_qty_in - stock.unit_qty_out < 0: -# raise StockError( -# "Stock `unit qty` is wrong. Unit qty IN cannot be less than unit qty OUT. " -# f"Got stock {stock.stock_identifier}." -# ) -# return stock - - -# def generate_code_with_checksum_from_id(id_number: int) -> str: -# bytes_id = id_number.to_bytes((id_number.bit_length() + 7) // 8, "big") -# code = base64.b32encode(bytes_id).decode("utf-8") -# code = code.rstrip("=") -# checksum = sum(ord(c) * (i + 1) for i, c in enumerate(code)) % 36 -# checksum_char = ( -# string.digits[checksum] if checksum < 10 else string.ascii_uppercase[checksum - 10] -# ) -# return code + checksum_char - - -# def decode_code_with_checksum(code: str) -> int: -# if code != add_checksum(code[:-1]): -# raise ChecksumError(f"Invalid checksum for code. Got {code}") -# padding_length = (8 - (len(code[:-1]) % 8)) % 8 -# padded_code = code[:-1] + ("=" * padding_length) -# try: -# decoded_bytes = base64.b32decode(padded_code) -# except Error as e: -# raise ValueError(f"Failed to decode string: {str(e)}") -# return int.from_bytes(decoded_bytes, "big") - - -# def add_checksum(code): -# checksum = sum(ord(c) * (i + 1) for i, c in enumerate(code)) % 36 -# checksum_char = ( -# string.digits[checksum] if checksum < 10 else string.ascii_uppercase[checksum - 10] -# ) -# return code + checksum_char +__all__ = ["get_rxrefill_model_cls", "get_rx_model_cls"] diff --git a/edc_pharmacy/utils/process_repack_request.py b/edc_pharmacy/utils/process_repack_request.py index 627bc01..404b103 100644 --- a/edc_pharmacy/utils/process_repack_request.py +++ b/edc_pharmacy/utils/process_repack_request.py @@ -1,20 +1,23 @@ from __future__ import annotations +from typing import TYPE_CHECKING from uuid import UUID +from celery import shared_task from django.apps import apps as django_apps from edc_utils import get_utcnow from ..exceptions import InsufficientStockError, RepackError +if TYPE_CHECKING: + from ..models import RepackRequest + +@shared_task def process_repack_request( repack_request_id: UUID | None = None, username: str | None = None -) -> None: - """Take from stock and fill container as new stock item. - - Do not change location here. - """ +) -> RepackRequest: + """Take from stock and fill container as new stock item.""" repack_request_model_cls = django_apps.get_model("edc_pharmacy.repackrequest") stock_model_cls = django_apps.get_model("edc_pharmacy.stock") repack_request = repack_request_model_cls.objects.get(id=repack_request_id) @@ -64,3 +67,8 @@ def process_repack_request( "modified", ] ) + repack_request.refresh_from_db() + return repack_request + + +__all__ = ["process_repack_request"] diff --git a/edc_pharmacy/utils/process_repack_request_queryset.py b/edc_pharmacy/utils/process_repack_request_queryset.py new file mode 100644 index 0000000..07a2fe9 --- /dev/null +++ b/edc_pharmacy/utils/process_repack_request_queryset.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from uuid import UUID + +from celery import shared_task +from edc_utils.celery import celery_is_active, run_task_sync_or_async + +from ..utils import process_repack_request + + +@shared_task +def process_repack_request_queryset( + repack_request_pks: list[UUID], username: str = None +) -> None: + if not celery_is_active(): + repack_request_pks = repack_request_pks[:1] + for pk in repack_request_pks: + run_task_sync_or_async(process_repack_request, repack_request_id=pk, username=username) + + return None + + +__all__ = ["process_repack_request_queryset"] diff --git a/edc_pharmacy/utils/stock_request/__init__.py b/edc_pharmacy/utils/stock_request/__init__.py index 8159f5d..029f44c 100644 --- a/edc_pharmacy/utils/stock_request/__init__.py +++ b/edc_pharmacy/utils/stock_request/__init__.py @@ -1,2 +1,4 @@ from .bulk_create_stock_request_items import bulk_create_stock_request_items from .remove_subjects_where_stock_on_site import remove_subjects_where_stock_on_site + +__all__ = ["bulk_create_stock_request_items", "remove_subjects_where_stock_on_site"] diff --git a/edc_pharmacy/utils/stock_request/bulk_create_stock_request_items.py b/edc_pharmacy/utils/stock_request/bulk_create_stock_request_items.py index cc25d0d..dbd7e02 100644 --- a/edc_pharmacy/utils/stock_request/bulk_create_stock_request_items.py +++ b/edc_pharmacy/utils/stock_request/bulk_create_stock_request_items.py @@ -56,3 +56,6 @@ def bulk_create_stock_request_items( if data: stock_request_item_model_cls.objects.bulk_create(data) return None + + +__all__ = ["bulk_create_stock_request_items"] diff --git a/edc_pharmacy/utils/stock_request/remove_subjects_where_stock_on_site.py b/edc_pharmacy/utils/stock_request/remove_subjects_where_stock_on_site.py index 8d5322f..f28e00c 100644 --- a/edc_pharmacy/utils/stock_request/remove_subjects_where_stock_on_site.py +++ b/edc_pharmacy/utils/stock_request/remove_subjects_where_stock_on_site.py @@ -35,3 +35,6 @@ def remove_subjects_where_stock_on_site(stock_request: StockRequest, df: pd.Data df["stock_qty"] = 0.0 df = df.reset_index(drop=True) return df + + +__all__ = ["remove_subjects_where_stock_on_site"] diff --git a/edc_pharmacy/utils/transfer_stock.py b/edc_pharmacy/utils/transfer_stock.py index 7c7162a..d3d569e 100644 --- a/edc_pharmacy/utils/transfer_stock.py +++ b/edc_pharmacy/utils/transfer_stock.py @@ -12,7 +12,7 @@ def transfer_stock( stock_transfer: StockTransfer, stock_codes: list[str], username: str | None = None -): +) -> tuple[list[str], list[str]]: stock_model_cls = django_apps.get_model("edc_pharmacy.stock") stock_transfer_item_model_cls = django_apps.get_model("edc_pharmacy.stocktransferitem") transferred, skipped_codes = [], [] @@ -36,3 +36,7 @@ def transfer_stock( stock.location = stock_transfer.to_location stock.save() transferred.append(stock_code) + return transferred, skipped_codes + + +__all__ = ["transfer_stock"] diff --git a/edc_pharmacy/utils/update_previous_refill_end_datetime.py b/edc_pharmacy/utils/update_previous_refill_end_datetime.py index 801e4b0..a070004 100644 --- a/edc_pharmacy/utils/update_previous_refill_end_datetime.py +++ b/edc_pharmacy/utils/update_previous_refill_end_datetime.py @@ -18,3 +18,6 @@ def update_previous_refill_end_datetime(instance): else: obj.refill_end_datetime = instance.refill_start_datetime - relativedelta(seconds=1) obj.save_base(update_fields=["refill_end_datetime"]) + + +__all__ = ["update_previous_refill_end_datetime"]