From abad94af4c5fb2f10b71ad208f45bbc43a6da850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johnny=20Marie=CC=81thoz?= Date: Wed, 22 Jan 2025 11:47:40 +0100 Subject: [PATCH] primeng: acquistion (in progress) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-by: Johnny MarieĢthoz Co-Authored-by: Pascal Repond --- Dockerfile.base | 2 +- INSTALL.rst | 5 +- data/acq_order_lines.json | 37 ++--- data/acq_orders.json | 16 +-- data/acq_receipts.json | 2 - .../acq_accounts/serializers/json.py | 9 ++ .../acquisition/acq_order_lines/api.py | 34 ++--- .../acquisition/acq_order_lines/dumpers.py | 2 +- .../acq_order_line-v0.0.1.json | 132 +++++++----------- .../acq_order_line-v0.0.1.json | 9 -- .../acquisition/acq_order_lines/models.py | 1 - .../acq_order_lines/permissions.py | 14 ++ .../modules/acquisition/acq_orders/api.py | 28 ++-- .../modules/acquisition/acq_orders/dumpers.py | 2 +- .../acquisition/acq_orders/extensions.py | 3 - .../acq_orders/acq_order-v0.0.1.json | 57 ++------ .../v7/acq_orders/acq_order-v0.0.1.json | 6 - .../acq_orders/serializers/__init__.py | 1 - .../acquisition/acq_orders/serializers/csv.py | 1 - .../acquisition/acq_receipt_lines/api.py | 26 +++- .../acq_receipt_lines/permissions.py | 6 + .../modules/acquisition/acq_receipts/api.py | 25 +++- .../acq_receipts/acq_receipt-v0.0.1.json | 48 +------ .../v7/acq_receipts/acq_receipt-v0.0.1.json | 3 - .../acquisition/acq_receipts/permissions.py | 6 + rero_ils/modules/cli/fixtures.py | 2 +- ...document_content_media_carrier-v0.0.1.json | 2 +- .../document_contribution_role-v0.0.1.json | 2 +- .../document_intended_audience-v0.0.1.json | 2 +- .../remote_entities/remote_entity-v0.0.1.json | 2 +- .../locations/location-v0.0.1.json | 10 -- .../patron_types/patron_type-v0.0.1.json | 66 +-------- rero_ils/modules/permissions.py | 34 +++++ .../stats_cfg/stat_cfg-v0.0.1.json | 20 ++- .../test_acq_order_lines_permissions.py | 80 +++++++++++ tests/api/acq_orders/test_acq_orders_rest.py | 3 - .../acq_orders/test_acq_orders_serializers.py | 2 +- tests/api/acq_orders/test_acq_orders_views.py | 4 +- .../test_acq_receipt_lines_rest.py | 97 ++++--------- .../test_acq_receipt_lines_rest_filtered.py | 59 ++++++++ .../test_acq_receipts_permissions.py | 79 +++++++++++ .../acq_receipts/test_acq_receipts_rest.py | 8 -- .../test_acquisition_reception_workflow.py | 9 +- .../acquisition/test_acquisition_scenarios.py | 2 - .../test_acquisition_serializers.py | 1 - tests/api/test_serializers.py | 4 +- tests/data/acquisition.json | 12 +- tests/data/data.json | 24 ++++ tests/fixtures/acquisition.py | 92 ++++++++---- tests/ui/acq_orders/test_acq_orders_api.py | 12 -- .../test_acq_receipt_lines_api.py | 1 - .../ui/acq_receipts/test_acq_receipts_api.py | 45 +++--- .../acq_receipts/test_acq_receipts_mapping.py | 12 ++ 53 files changed, 623 insertions(+), 538 deletions(-) create mode 100644 tests/api/acq_receipt_lines/test_acq_receipt_lines_rest_filtered.py diff --git a/Dockerfile.base b/Dockerfile.base index 497cfeb8d6..001b855274 100644 --- a/Dockerfile.base +++ b/Dockerfile.base @@ -20,7 +20,7 @@ FROM python:3.9-slim-bullseye # require debian packages RUN apt-get update -y && apt-get upgrade -y RUN apt-get install --no-install-recommends -y git vim-tiny curl gcc gnupg libc6-dev procps imagemagick && rm -rf /var/lib/apt/lists/* -RUN pip install --upgrade setuptools wheel pip poetry +RUN pip install --upgrade setuptools wheel pip poetry && poetry self add poetry-plugin-shell # # uwsgi uwsgitop uwsgi-tools diff --git a/INSTALL.rst b/INSTALL.rst index d9c900f1b4..106657af01 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -58,7 +58,10 @@ among other things. $ pyenv install 3.9.7 $ cd rero-ils $ pyenv local 3.9.7 - $ sudo pip install poetry + $ curl -sSL https://install.python-poetry.org | python3 - + $ optional: poetry self add poetry-plugin-shell + +See: https://python-poetry.org/docs#installation for more detail. Next, ``cd`` into the project directory and bootstrap the instance (this will install all Python dependencies and build all static assets): diff --git a/data/acq_order_lines.json b/data/acq_order_lines.json index d27ee003e6..cb6c8dcf4b 100644 --- a/data/acq_order_lines.json +++ b/data/acq_order_lines.json @@ -4,7 +4,6 @@ "priority": 0, "quantity": 3, "amount": 25, - "exchange_rate": 1, "notes": [ { "type": "staff_note", @@ -23,15 +22,13 @@ "library": { "$ref": "https://bib.rero.ch/api/libraries/1" }, - "is_cancelled": false, - "send_now": true + "is_cancelled": false }, { "pid": "2", "priority": 0, "quantity": 15, "amount": 5, - "exchange_rate": 1, "notes": [ { "type": "staff_note", @@ -50,15 +47,13 @@ "library": { "$ref": "https://bib.rero.ch/api/libraries/1" }, - "is_cancelled": false, - "send_now": true + "is_cancelled": false }, { "pid": "3", "priority": 0, "quantity": 2, "amount": 100, - "exchange_rate": 1, "notes": [ { "type": "staff_note", @@ -77,15 +72,13 @@ "library": { "$ref": "https://bib.rero.ch/api/libraries/1" }, - "is_cancelled": false, - "send_now": true + "is_cancelled": false }, { "pid": "4", "priority": 0, "quantity": 5, "amount": 10, - "exchange_rate": 1, "acq_account": { "$ref": "https://bib.rero.ch/api/acq_accounts/7" }, @@ -98,15 +91,13 @@ "library": { "$ref": "https://bib.rero.ch/api/libraries/2" }, - "is_cancelled": false, - "send_now": true + "is_cancelled": false }, { "pid": "5", "priority": 5, "quantity": 1, "amount": 100, - "exchange_rate": 1, "notes": [ { "type": "staff_note", @@ -125,15 +116,13 @@ "library": { "$ref": "https://bib.rero.ch/api/libraries/2" }, - "is_cancelled": false, - "send_now": true + "is_cancelled": false }, { "pid": "6", "priority": 1, "quantity": 5, "amount": 7, - "exchange_rate": 1, "acq_account": { "$ref": "https://bib.rero.ch/api/acq_accounts/14" }, @@ -146,15 +135,13 @@ "library": { "$ref": "https://bib.rero.ch/api/libraries/3" }, - "is_cancelled": false, - "send_now": true + "is_cancelled": false }, { "pid": "7", "priority": 1, "quantity": 15, "amount": 12, - "exchange_rate": 1, "acq_account": { "$ref": "https://bib.rero.ch/api/acq_accounts/20" }, @@ -167,15 +154,13 @@ "library": { "$ref": "https://bib.rero.ch/api/libraries/4" }, - "is_cancelled": false, - "send_now": true + "is_cancelled": false }, { "pid": "8", "priority": 1, "quantity": 10, "amount": 20, - "exchange_rate": 1, "acq_account": { "$ref": "https://bib.rero.ch/api/acq_accounts/4" }, @@ -188,15 +173,13 @@ "library": { "$ref": "https://bib.rero.ch/api/libraries/1" }, - "is_cancelled": false, - "send_now": true + "is_cancelled": false }, { "pid": "9", "priority": 0, "quantity": 3, "amount": 22, - "exchange_rate": 1, "acq_account": { "$ref": "https://bib.rero.ch/api/acq_accounts/2" }, @@ -216,7 +199,6 @@ "priority": 0, "quantity": 3, "amount": 25, - "exchange_rate": 1, "notes": [ { "type": "staff_note", @@ -235,7 +217,6 @@ "library": { "$ref": "https://bib.rero.ch/api/libraries/1" }, - "is_cancelled": false, - "order_date": "2021-07-20" + "is_cancelled": false } ] diff --git a/data/acq_orders.json b/data/acq_orders.json index a5ed434031..31ab0fc203 100644 --- a/data/acq_orders.json +++ b/data/acq_orders.json @@ -2,7 +2,6 @@ { "pid": "1", "reference": "AOSTE-CANT1-POL-1", - "type": "monograph", "currency": "EUR", "notes": [ { @@ -18,12 +17,12 @@ }, "organisation": { "$ref": "https://bib.rero.ch/api/organisations/1" - } + }, + "send_now": true }, { "pid": "2", "reference": "AOSTE-CANT1-POL-2", - "type": "monograph", "currency": "EUR", "notes": [ { @@ -39,12 +38,12 @@ }, "organisation": { "$ref": "https://bib.rero.ch/api/organisations/1" - } + }, + "send_now": true }, { "pid": "3", "reference": "AOSTE-CANT2-POL-1", - "type": "monograph", "currency": "EUR", "notes": [ { @@ -65,7 +64,6 @@ { "pid": "4", "reference": "AOSTE-AVISE-POL-1", - "type": "monograph", "currency": "EUR", "notes": [ { @@ -86,7 +84,6 @@ { "pid": "5", "reference": "AOSTE-LYCEE-POL-1", - "type": "monograph", "currency": "EUR", "notes": [ { @@ -107,7 +104,6 @@ { "pid": "6", "reference": "AOSTE-CANT1-POL-6", - "type": "monograph", "currency": "EUR", "library": { "$ref": "https://bib.rero.ch/api/libraries/1" @@ -122,7 +118,6 @@ { "pid": "7", "reference": "AOSTE-CANT1-POL-2021-1", - "type": "monograph", "currency": "EUR", "notes": [ { @@ -138,6 +133,7 @@ }, "organisation": { "$ref": "https://bib.rero.ch/api/organisations/1" - } + }, + "send_now": true } ] diff --git a/data/acq_receipts.json b/data/acq_receipts.json index acb05df625..b984947fda 100644 --- a/data/acq_receipts.json +++ b/data/acq_receipts.json @@ -5,7 +5,6 @@ "$ref": "https://bib.rero.ch/api/acq_orders/1" }, "reference": "AOSTE-CANT1-POL-1", - "exchange_rate": 1, "notes": [ { "type": "staff_note", @@ -25,7 +24,6 @@ "$ref": "https://bib.rero.ch/api/acq_orders/7" }, "reference": "AOSTE-CANT1-POL-2021-1", - "exchange_rate": 1, "notes": [ { "type": "staff_note", diff --git a/rero_ils/modules/acquisition/acq_accounts/serializers/json.py b/rero_ils/modules/acquisition/acq_accounts/serializers/json.py index ad63d2c1b8..c48afcd1eb 100644 --- a/rero_ils/modules/acquisition/acq_accounts/serializers/json.py +++ b/rero_ils/modules/acquisition/acq_accounts/serializers/json.py @@ -26,6 +26,15 @@ class AcqAccountJSONSerializer(ACQJSONSerializer): """Serializer for RERO-ILS `AcqAccount` records as JSON.""" + def _postprocess_search_hit(self, hit: dict) -> None: + """Post-process each hit of a search result.""" + hit["metadata"]["number_of_children"] = ( + AcqAccountsSearch() + .filter("term", parent__pid=hit["metadata"]["pid"]) + .count() + ) + super()._postprocess_search_hit(hit) + def preprocess_record(self, pid, record, links_factory=None, **kwargs): """Prepare a record and persistent identifier for serialization.""" # Add some ES stored keys into response diff --git a/rero_ils/modules/acquisition/acq_order_lines/api.py b/rero_ils/modules/acquisition/acq_order_lines/api.py index 5f43a809c7..29334e271f 100644 --- a/rero_ils/modules/acquisition/acq_order_lines/api.py +++ b/rero_ils/modules/acquisition/acq_order_lines/api.py @@ -19,11 +19,9 @@ """API for manipulating Acquisition Order Line.""" from copy import deepcopy -from datetime import datetime from functools import partial from flask_babel import gettext as _ -from werkzeug.utils import cached_property from rero_ils.modules.acquisition.api import AcquisitionIlsRecord from rero_ils.modules.api import IlsRecordsIndexer, IlsRecordsSearch @@ -89,7 +87,17 @@ def extended_validation(self, **kwargs): note_types = [note.get("type") for note in self.get("notes", [])] if len(note_types) != len(set(note_types)): return _("Can not have multiple notes of the same type.") - + from rero_ils.modules.acquisition.acq_orders.api import AcqOrder + from rero_ils.modules.acquisition.acq_orders.models import AcqOrderStatus + + order_status = AcqOrder.get_status_by_pid(self.order_pid) + if order_status not in [ + AcqOrderStatus.CANCELLED, + AcqOrderStatus.PENDING, + ]: + return _( + f"Can not create an order line with an order with a wrong status {order_status}." + ) return True @classmethod @@ -149,11 +157,6 @@ def order(self): """Shortcut to the order of the order line.""" return extracted_data_from_ref(self.get("acq_order"), data="record") - @property - def order_date(self): - """Shortcut for acquisition order send date.""" - return self.get("order_date") - @property def is_cancelled(self): """Shortcut for acquisition order is_cancelled falg.""" @@ -223,19 +226,6 @@ def unreceived_quantity(self): """Get quantity of unreceived ordered_items for a line order.""" return self.quantity - self.received_quantity - @cached_property - def receipt_date(self): - """Get the first reception date for one item of this order line.""" - from rero_ils.modules.acquisition.acq_receipt_lines.api import ( - AcqReceiptLinesSearch, - ) - - search = AcqReceiptLinesSearch().filter("term", acq_order_line__pid=self.pid) - search.aggs.metric("min_receipt_date", "min", field="receipt_date") - results = search.execute() - epoch = results.aggregations.min_receipt_date.value / 1000 - return datetime.fromtimestamp(epoch) - @property def status(self): """Calculate the order line status. @@ -252,7 +242,7 @@ def status(self): return AcqOrderLineStatus.CANCELLED status = ( AcqOrderLineStatus.ORDERED - if self.order_date + if self.order.get("order_date") else AcqOrderLineStatus.APPROVED ) diff --git a/rero_ils/modules/acquisition/acq_order_lines/dumpers.py b/rero_ils/modules/acquisition/acq_order_lines/dumpers.py index d9fc056c51..32b7b2b867 100644 --- a/rero_ils/modules/acquisition/acq_order_lines/dumpers.py +++ b/rero_ils/modules/acquisition/acq_order_lines/dumpers.py @@ -42,7 +42,7 @@ def dump(self, record, data): :param data: The initial dump data passed in by ``record.dumps()``. """ # Keep only some attributes from AcqOrderLine object initial dump. - for attr in ["pid", "status", "order_date", "quantity"]: + for attr in ["pid", "status", "quantity"]: if value := record.get(attr): data.update({attr: value}) diff --git a/rero_ils/modules/acquisition/acq_order_lines/jsonschemas/acq_order_lines/acq_order_line-v0.0.1.json b/rero_ils/modules/acquisition/acq_order_lines/jsonschemas/acq_order_lines/acq_order_line-v0.0.1.json index bc00fb95f4..cc1a700691 100644 --- a/rero_ils/modules/acquisition/acq_order_lines/jsonschemas/acq_order_lines/acq_order_line-v0.0.1.json +++ b/rero_ils/modules/acquisition/acq_order_lines/jsonschemas/acq_order_lines/acq_order_line-v0.0.1.json @@ -8,11 +8,8 @@ "acq_account", "document", "priority", - "order_date", - "receipt_date", "quantity", "amount", - "exchange_rate", "notes", "is_cancelled" ], @@ -44,14 +41,10 @@ "type": "integer", "minimum": 0, "maximum": 5, - "default": 0, "widget": { "formlyConfig": { "props": { - "hide": true, - "navigation": { - "essential": true - } + "itemCssClass": "col-12 md:col-3 md:col-offset-right-9" } } } @@ -60,7 +53,14 @@ "title": "Quantity", "type": "integer", "default": 1, - "minimum": 1 + "minimum": 1, + "widget": { + "formlyConfig": { + "props": { + "itemCssClass": "col-12 md:col-6" + } + } + } }, "amount": { "title": "Amount", @@ -70,8 +70,8 @@ "widget": { "formlyConfig": { "props": { - "hideLabel": true, - "fieldMap": "amount" + "fieldMap": "amount", + "itemCssClass": "col-12 md:col-6" } } } @@ -82,26 +82,10 @@ "default": 0, "minimum": 0 }, - "exchange_rate": { - "title": "Exchange rate", - "type": "number", - "minimum": 0, - "widget": { - "formlyConfig": { - "props": { - "hideLabel": true, - "hide": true, - "navigation": { - "essential": true - } - } - } - } - }, "notes": { "title": "Notes", "type": "array", - "minItems": 1, + "minItems": 0, "maxItems": 2, "items": { "type": "object", @@ -161,10 +145,8 @@ }, "widget": { "formlyConfig": { - "wrappers": [ - "card" - ], "props": { + "itemCssClass": "col-12", "validation": { "validators": { "uniqueValueKeysInObject": { @@ -176,61 +158,11 @@ "messages": { "uniqueValueKeysInObjectMessage": "Only one note per type is allowed" } - }, - "hide": true, - "navigation": { - "essential": true } } } } }, - "order_date": { - "title": "Order date", - "type": "string", - "format": "date", - "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "widget": { - "formlyConfig": { - "type": "datePicker", - "expressions": { - "props.required": "field?.parent?.model?.status === 'ordered' || field?.parent?.model?.status === 'received'", - "hide": "field?.parent?.model?.status !== 'ordered' && field?.parent?.model?.status !== 'received'" - }, - "props": { - "validation": { - "messages": { - "patternMessage": "Should be in the following format: 2022-12-31 (YYYY-MM-DD)." - } - }, - "placeholder": "Select a date" - } - } - } - }, - "receipt_date": { - "title": "Receipt date", - "type": "string", - "format": "date", - "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "widget": { - "formlyConfig": { - "type": "datePicker", - "expressions": { - "props.required": "field?.parent?.model?.status === 'received'", - "hide": "field?.parent?.model?.status !== 'received'" - }, - "props": { - "validation": { - "messages": { - "patternMessage": "Should be in the following format: 2022-12-31 (YYYY-MM-DD)." - } - }, - "placeholder": "Select a date" - } - } - } - }, "acq_account": { "title": "Acquisition account", "type": "object", @@ -253,6 +185,13 @@ } } } + }, + "widget": { + "formlyConfig": { + "props": { + "itemCssClass": "col-12" + } + } } }, "acq_order": { @@ -264,6 +203,13 @@ "type": "string", "pattern": "^https://bib.rero.ch/api/acq_orders/.*?$" } + }, + "widget": { + "formlyConfig": { + "props": { + "itemCssClass": "col-12" + } + } } }, "document": { @@ -280,6 +226,7 @@ "widget": { "formlyConfig": { "type": "remoteAutoComplete", + "hideLabel": true, "props": { "validation": { "messages": { @@ -294,6 +241,13 @@ } } } + }, + "widget": { + "formlyConfig": { + "props": { + "itemCssClass": "col-12" + } + } } }, "organisation": { @@ -330,7 +284,21 @@ "title": "Cancelled", "description": "If checked this order line should be considered as cancelled.", "type": "boolean", - "default": false + "default": false, + "widget": { + "formlyConfig": { + "props": { + "itemCssClass": "col-12" + } + } + } + } + }, + "widget": { + "formlyConfig": { + "props": { + "containerCssClass": "grid grid-nogutter-y" + } } } } diff --git a/rero_ils/modules/acquisition/acq_order_lines/mappings/v7/acq_order_lines/acq_order_line-v0.0.1.json b/rero_ils/modules/acquisition/acq_order_lines/mappings/v7/acq_order_lines/acq_order_line-v0.0.1.json index d9b5167049..78a7eed3bd 100644 --- a/rero_ils/modules/acquisition/acq_order_lines/mappings/v7/acq_order_lines/acq_order_line-v0.0.1.json +++ b/rero_ils/modules/acquisition/acq_order_lines/mappings/v7/acq_order_lines/acq_order_line-v0.0.1.json @@ -90,9 +90,6 @@ "total_unreceived_amount": { "type": "float" }, - "exchange_rate": { - "type": "float" - }, "notes": { "properties": { "type": { @@ -103,12 +100,6 @@ } } }, - "order_date": { - "type": "date" - }, - "receipt_date": { - "type": "date" - }, "is_cancelled": { "type": "boolean" }, diff --git a/rero_ils/modules/acquisition/acq_order_lines/models.py b/rero_ils/modules/acquisition/acq_order_lines/models.py index 8ff340004b..6871d42d8b 100644 --- a/rero_ils/modules/acquisition/acq_order_lines/models.py +++ b/rero_ils/modules/acquisition/acq_order_lines/models.py @@ -51,7 +51,6 @@ class AcqOrderLineStatus: ORDERED = "ordered" RECEIVED = "received" PARTIALLY_RECEIVED = "partially_received" - RECEIVED_STATUSES = [RECEIVED, PARTIALLY_RECEIVED] diff --git a/rero_ils/modules/acquisition/acq_order_lines/permissions.py b/rero_ils/modules/acquisition/acq_order_lines/permissions.py index 83f95fe760..fd7b74c87a 100644 --- a/rero_ils/modules/acquisition/acq_order_lines/permissions.py +++ b/rero_ils/modules/acquisition/acq_order_lines/permissions.py @@ -19,9 +19,11 @@ """Permissions for Acquisition order line.""" from invenio_access import action_factory +from rero_ils.modules.acquisition.acq_orders.models import AcqOrderStatus from rero_ils.modules.permissions import ( AllowedByAction, AllowedByActionRestrictByManageableLibrary, + DisallowedByOrderStatus, DisallowedIfRollovered, RecordPermissionPolicy, ) @@ -49,8 +51,20 @@ class AcqOrderLinePermissionPolicy(RecordPermissionPolicy): can_update = [ AllowedByActionRestrictByManageableLibrary(update_action), DisallowedIfRollovered(AcqOrderLine), + DisallowedByOrderStatus( + AcqOrderLine, + [ + AcqOrderStatus.CANCELLED, + AcqOrderStatus.PENDING, + AcqOrderStatus.ORDERED, + AcqOrderStatus.PARTIALLY_RECEIVED, + ], + ), ] can_delete = [ AllowedByActionRestrictByManageableLibrary(delete_action), DisallowedIfRollovered(AcqOrderLine), + DisallowedByOrderStatus( + AcqOrderLine, [AcqOrderStatus.CANCELLED, AcqOrderStatus.PENDING] + ), ] diff --git a/rero_ils/modules/acquisition/acq_orders/api.py b/rero_ils/modules/acquisition/acq_orders/api.py index 1dd3192115..9ab7a54456 100644 --- a/rero_ils/modules/acquisition/acq_orders/api.py +++ b/rero_ils/modules/acquisition/acq_orders/api.py @@ -155,8 +155,13 @@ def status(self): is PARTIALLY_RECEIVED or RECEIVED. RECEIVED: if all related order lines has RECEIVED status. """ + return self.get_status_by_pid(self.pid) + + @classmethod + def get_status_by_pid(cls, pid): + """.""" status = AcqOrderStatus.PENDING - search = AcqOrderLinesSearch().filter("term", acq_order__pid=self.pid) + search = AcqOrderLinesSearch().filter("term", acq_order__pid=pid) search.aggs.bucket("status", "terms", field="status") results = search.execute() statuses = [hit.key for hit in results.aggregations.status.buckets] @@ -185,19 +190,6 @@ def status(self): return status - @property - def order_date(self): - """Get the order date of this order.""" - result = ( - AcqOrderLinesSearch() - .filter("term", acq_order__pid=self.pid) - .filter("exists", field="order_date") - .source(["order_date"]) - .scan() - ) - dates = [hit.order_date for hit in result] - return next(iter(dates or []), None) - @property def item_quantity(self): """Get the total of item quantity for this order.""" @@ -485,11 +477,13 @@ def send_order(self, emails=None): # notification metadata (status, process_date, ...) if dispatcher_result.get("sent", 0): order_date = datetime.now().strftime("%Y-%m-%d") + self["order_date"] = order_date + record = self.update(self, dbcommit=True, reindex=False) order_lines = self.get_order_lines(includes=[AcqOrderLineStatus.APPROVED]) for order_line in order_lines: - order_line["order_date"] = order_date - order_line.update(order_line, dbcommit=True, reindex=True) - self.reindex() + order_line.reindex() + record.reindex() + notif = Notification.get_record(notif.id) return notif diff --git a/rero_ils/modules/acquisition/acq_orders/dumpers.py b/rero_ils/modules/acquisition/acq_orders/dumpers.py index 57dd3f63d0..0be187e165 100644 --- a/rero_ils/modules/acquisition/acq_orders/dumpers.py +++ b/rero_ils/modules/acquisition/acq_orders/dumpers.py @@ -46,7 +46,7 @@ def dump(self, record, data): data.update( { "reference": record.get("reference"), - "order_date": record.order_date or today, + "order_date": record.get("order_date", today), "note": record.get_note(AcqOrderNoteType.VENDOR), } ) diff --git a/rero_ils/modules/acquisition/acq_orders/extensions.py b/rero_ils/modules/acquisition/acq_orders/extensions.py index c2a418a8b8..08a247fb41 100644 --- a/rero_ils/modules/acquisition/acq_orders/extensions.py +++ b/rero_ils/modules/acquisition/acq_orders/extensions.py @@ -31,8 +31,6 @@ def pre_dump(self, record, dumper=None): :param record: the record metadata. :param dumper: the record dumper. """ - if record.order_date: - record["order_date"] = record.order_date record["account_statement"] = record.get_account_statement() record["status"] = record.status @@ -44,7 +42,6 @@ def pre_load(self, data, loader=None): """ data.pop("account_statement", None) data.pop("status", None) - data.pop("order_date", None) def pre_delete(self, record, force=False): """Called before a record is deleted. diff --git a/rero_ils/modules/acquisition/acq_orders/jsonschemas/acq_orders/acq_order-v0.0.1.json b/rero_ils/modules/acquisition/acq_orders/jsonschemas/acq_orders/acq_order-v0.0.1.json index 96b21fd5d3..8796fc5b43 100644 --- a/rero_ils/modules/acquisition/acq_orders/jsonschemas/acq_orders/acq_order-v0.0.1.json +++ b/rero_ils/modules/acquisition/acq_orders/jsonschemas/acq_orders/acq_order-v0.0.1.json @@ -7,15 +7,14 @@ "propertiesOrder": [ "vendor", "reference", - "type", + "order_date", "notes" ], "required": [ "$schema", "pid", "vendor", - "library", - "type" + "library" ], "properties": { "$schema": { @@ -35,56 +34,22 @@ "type": "string", "minLength": 3 }, - "type": { - "title": "Type", + "currency": { + "$ref": "https://bib.rero.ch/schemas/common/currency-v0.0.1.json#/currency" + }, + "order_date": { + "title": "Order date", "type": "string", - "enum": [ - "serial", - "monograph", - "standing_order", - "monographic_set", - "planned_order", - "multi_volume" - ], - "default": "monograph", + "format": "date", + "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "widget": { "formlyConfig": { - "type": "select", - "props": { - "sort": true, - "options": [ - { - "value": "monograph", - "label": "monograph" - }, - { - "value": "serial", - "label": "serial" - }, - { - "value": "standing_order", - "label": "standing_order" - }, - { - "value": "monographic_set", - "label": "monographic_set" - }, - { - "value": "planned_order", - "label": "planned_order" - }, - { - "value": "multi_volume", - "label": "multi_volume" - } - ] + "expressions": { + "hide": "true" } } } }, - "currency": { - "$ref": "https://bib.rero.ch/schemas/common/currency-v0.0.1.json#/currency" - }, "notes": { "title": "Notes", "type": "array", diff --git a/rero_ils/modules/acquisition/acq_orders/mappings/v7/acq_orders/acq_order-v0.0.1.json b/rero_ils/modules/acquisition/acq_orders/mappings/v7/acq_orders/acq_order-v0.0.1.json index 881556a802..647346c2ee 100644 --- a/rero_ils/modules/acquisition/acq_orders/mappings/v7/acq_orders/acq_order-v0.0.1.json +++ b/rero_ils/modules/acquisition/acq_orders/mappings/v7/acq_orders/acq_order-v0.0.1.json @@ -73,9 +73,6 @@ } } }, - "type": { - "type": "keyword" - }, "status": { "type": "keyword" }, @@ -123,9 +120,6 @@ "pid": { "type": "keyword" }, - "order_date": { - "type": "date" - }, "quantity": { "type": "integer" }, diff --git a/rero_ils/modules/acquisition/acq_orders/serializers/__init__.py b/rero_ils/modules/acquisition/acq_orders/serializers/__init__.py index 17a8870f07..b8899420dc 100644 --- a/rero_ils/modules/acquisition/acq_orders/serializers/__init__.py +++ b/rero_ils/modules/acquisition/acq_orders/serializers/__init__.py @@ -44,7 +44,6 @@ "order_date", "order_staff_note", "order_vendor_note", - "order_type", "order_status", "vendor_name", "document_pid", diff --git a/rero_ils/modules/acquisition/acq_orders/serializers/csv.py b/rero_ils/modules/acquisition/acq_orders/serializers/csv.py index 9ae121e43f..9d41b181cc 100644 --- a/rero_ils/modules/acquisition/acq_orders/serializers/csv.py +++ b/rero_ils/modules/acquisition/acq_orders/serializers/csv.py @@ -84,7 +84,6 @@ def serialize_search( "order_pid": "pid", "order_reference": "reference", "order_date": "order_date", - "order_type": "type", "order_status": "status", } diff --git a/rero_ils/modules/acquisition/acq_receipt_lines/api.py b/rero_ils/modules/acquisition/acq_receipt_lines/api.py index 0de0522986..4d8e9cdcaa 100644 --- a/rero_ils/modules/acquisition/acq_receipt_lines/api.py +++ b/rero_ils/modules/acquisition/acq_receipt_lines/api.py @@ -21,6 +21,7 @@ from copy import deepcopy from functools import partial +from flask_babel import gettext as _ from werkzeug.utils import cached_property from rero_ils.modules.acquisition.api import AcquisitionIlsRecord @@ -85,6 +86,24 @@ def create( cls._build_additional_refs(data) return super().create(data, id_, delete_pid, dbcommit, reindex, **kwargs) + def extended_validation(self, **kwargs): + """Add additional record validation. + + :return: False if + """ + from rero_ils.modules.acquisition.acq_orders.api import AcqOrder + from rero_ils.modules.acquisition.acq_orders.models import AcqOrderStatus + + order_status = AcqOrder.get_status_by_pid(self.order_pid) + if order_status not in [ + AcqOrderStatus.ORDERED, + AcqOrderStatus.PARTIALLY_RECEIVED, + ]: + return _( + f"Can not create a receipt with an order with a wrong status {order_status}." + ) + return True + def update(self, data, commit=True, dbcommit=True, reindex=True): """Update Acquisition Receipt Line record.""" # TODO :: try to find a better way to load original record. @@ -129,6 +148,11 @@ def order_line(self): """Shortcut for related acquisition order line record.""" return extracted_data_from_ref(self.get("acq_order_line"), data="record") + @property + def order_pid(self): + """Shortcut for related acquisition order pid.""" + return self.receipt.order_pid + @property def is_active(self): """Check if the receipt line should be considered as active. @@ -157,7 +181,7 @@ def amount(self): def total_amount(self): """Shortcut for related acquisition total_amount.""" vat_factor = (100 + self.get("vat_rate", 0)) / 100 - total = self.amount * self.receipt.exchange_rate * self.quantity * vat_factor + total = self.amount * self.quantity * vat_factor return round(total, 2) @property diff --git a/rero_ils/modules/acquisition/acq_receipt_lines/permissions.py b/rero_ils/modules/acquisition/acq_receipt_lines/permissions.py index c0a4989041..e1f5598963 100644 --- a/rero_ils/modules/acquisition/acq_receipt_lines/permissions.py +++ b/rero_ils/modules/acquisition/acq_receipt_lines/permissions.py @@ -19,9 +19,11 @@ """Permissions for Acquisition receipt line.""" from invenio_access import action_factory +from rero_ils.modules.acquisition.acq_orders.models import AcqOrderStatus from rero_ils.modules.permissions import ( AllowedByAction, AllowedByActionRestrictByManageableLibrary, + DisallowedByOrderStatus, DisallowedIfRollovered, RecordPermissionPolicy, ) @@ -49,8 +51,12 @@ class AcqReceiptLinePermissionPolicy(RecordPermissionPolicy): can_update = [ AllowedByActionRestrictByManageableLibrary(update_action), DisallowedIfRollovered(AcqReceiptLine), + DisallowedByOrderStatus(AcqReceiptLine, [AcqOrderStatus.PARTIALLY_RECEIVED]), ] can_delete = [ AllowedByActionRestrictByManageableLibrary(delete_action), DisallowedIfRollovered(AcqReceiptLine), + DisallowedByOrderStatus( + AcqReceiptLine, [AcqOrderStatus.PARTIALLY_RECEIVED, AcqOrderStatus.RECEIVED] + ), ] diff --git a/rero_ils/modules/acquisition/acq_receipts/api.py b/rero_ils/modules/acquisition/acq_receipts/api.py index 08db28b7eb..78f2a2fae3 100644 --- a/rero_ils/modules/acquisition/acq_receipts/api.py +++ b/rero_ils/modules/acquisition/acq_receipts/api.py @@ -21,6 +21,8 @@ from copy import deepcopy from functools import partial +from flask_babel import gettext as _ + from rero_ils.modules.acquisition.acq_receipt_lines.api import ( AcqReceiptLine, AcqReceiptLinesSearch, @@ -140,6 +142,24 @@ def update(self, data, commit=True, dbcommit=True, reindex=True): account.reindex() return self + def extended_validation(self, **kwargs): + """Add additional record validation. + + :return: False if + """ + from rero_ils.modules.acquisition.acq_orders.api import AcqOrder + from rero_ils.modules.acquisition.acq_orders.models import AcqOrderStatus + + order_status = AcqOrder.get_status_by_pid(self.order_pid) + if order_status not in [ + AcqOrderStatus.ORDERED, + AcqOrderStatus.PARTIALLY_RECEIVED, + ]: + return _( + f"Can not create a receipt with an order with a wrong status {order_status}." + ) + return True + @classmethod def _build_additional_refs(cls, data): """Build $ref for the organisation of the acquisition receipt.""" @@ -242,11 +262,6 @@ def amount_adjustments(self): """Shortcut to get receipt amount adjustments.""" return self.get("amount_adjustments", []) - @property - def exchange_rate(self): - """Shortcut to get receipt exchange_rate.""" - return self.get("exchange_rate") - @property def total_amount(self): """Get this acquisition receipt total amount. diff --git a/rero_ils/modules/acquisition/acq_receipts/jsonschemas/acq_receipts/acq_receipt-v0.0.1.json b/rero_ils/modules/acquisition/acq_receipts/jsonschemas/acq_receipts/acq_receipt-v0.0.1.json index 2731a3ab60..7679ddc466 100644 --- a/rero_ils/modules/acquisition/acq_receipts/jsonschemas/acq_receipts/acq_receipt-v0.0.1.json +++ b/rero_ils/modules/acquisition/acq_receipts/jsonschemas/acq_receipts/acq_receipt-v0.0.1.json @@ -6,15 +6,13 @@ "additionalProperties": false, "propertiesOrder": [ "reference", - "exchange_rate", "amount_adjustments", "notes" ], "required": [ "$schema", "pid", - "acq_order", - "exchange_rate" + "acq_order" ], "properties": { "$schema": { @@ -47,32 +45,7 @@ "reference": { "title": "Reference", "type": "string", - "minLength": 3, - "widget": { - "formlyConfig": { - "props": { - "navigation": { - "essential": true - } - } - } - } - }, - "exchange_rate": { - "title": "Exchange rate", - "type": "number", - "exclusiveMinimum": 0, - "default": 1, - "widget": { - "formlyConfig": { - "props": { - "hide": true, - "navigation": { - "essential": true - } - } - } - } + "minLength": 3 }, "amount_adjustments": { "title": "Additional fees, discount and credit", @@ -127,16 +100,6 @@ } } } - }, - "widget": { - "formlyConfig": { - "props": { - "hide": true, - "navigation": { - "essential": true - } - } - } } }, "notes": { @@ -197,9 +160,6 @@ }, "widget": { "formlyConfig": { - "wrappers": [ - "card" - ], "props": { "validation": { "validators": { @@ -212,10 +172,6 @@ "messages": { "uniqueValueKeysInObjectMessage": "Only one note per type is allowed" } - }, - "hide": true, - "navigation": { - "essential": true } } } diff --git a/rero_ils/modules/acquisition/acq_receipts/mappings/v7/acq_receipts/acq_receipt-v0.0.1.json b/rero_ils/modules/acquisition/acq_receipts/mappings/v7/acq_receipts/acq_receipt-v0.0.1.json index 256fdeddd3..4d93ebe01b 100644 --- a/rero_ils/modules/acquisition/acq_receipts/mappings/v7/acq_receipts/acq_receipt-v0.0.1.json +++ b/rero_ils/modules/acquisition/acq_receipts/mappings/v7/acq_receipts/acq_receipt-v0.0.1.json @@ -42,9 +42,6 @@ } } }, - "exchange_rate": { - "type": "float" - }, "quantity": { "type": "integer" }, diff --git a/rero_ils/modules/acquisition/acq_receipts/permissions.py b/rero_ils/modules/acquisition/acq_receipts/permissions.py index 0eebba1d1a..c267bc48e9 100644 --- a/rero_ils/modules/acquisition/acq_receipts/permissions.py +++ b/rero_ils/modules/acquisition/acq_receipts/permissions.py @@ -19,9 +19,11 @@ """Permissions for Acquisition receipt.""" from invenio_access import action_factory +from rero_ils.modules.acquisition.acq_orders.models import AcqOrderStatus from rero_ils.modules.permissions import ( AllowedByAction, AllowedByActionRestrictByManageableLibrary, + DisallowedByOrderStatus, DisallowedIfRollovered, RecordPermissionPolicy, ) @@ -49,8 +51,12 @@ class AcqReceiptPermissionPolicy(RecordPermissionPolicy): can_update = [ AllowedByActionRestrictByManageableLibrary(update_action), DisallowedIfRollovered(AcqReceipt), + DisallowedByOrderStatus(AcqReceipt, [AcqOrderStatus.PARTIALLY_RECEIVED]), ] can_delete = [ AllowedByActionRestrictByManageableLibrary(delete_action), DisallowedIfRollovered(AcqReceipt), + DisallowedByOrderStatus( + AcqReceipt, [AcqOrderStatus.PARTIALLY_RECEIVED, AcqOrderStatus.RECEIVED] + ), ] diff --git a/rero_ils/modules/cli/fixtures.py b/rero_ils/modules/cli/fixtures.py index f2b8a96539..6f81060c3e 100644 --- a/rero_ils/modules/cli/fixtures.py +++ b/rero_ils/modules/cli/fixtures.py @@ -212,7 +212,7 @@ def create( record["name"] = year record["start_date"] = f"{year}-01-01" record["end_date"] = f"{year}-12-31" - elif pid_type == "acol" and record.pop("send_now", None): + elif pid_type == "acor" and record.pop("send_now", None): # ensure all orders are sent record["order_date"] = f"{order_date}" elif pid_type == "acrl" and record.pop("receive_now", None): diff --git a/rero_ils/modules/documents/jsonschemas/documents/document_content_media_carrier-v0.0.1.json b/rero_ils/modules/documents/jsonschemas/documents/document_content_media_carrier-v0.0.1.json index 4f08698787..a5cc64c4e1 100644 --- a/rero_ils/modules/documents/jsonschemas/documents/document_content_media_carrier-v0.0.1.json +++ b/rero_ils/modules/documents/jsonschemas/documents/document_content_media_carrier-v0.0.1.json @@ -812,7 +812,7 @@ ], "widget": { "formlyConfig": { - "type": "select", + "type": "multi-select", "props": { "sort": true, "options": [ diff --git a/rero_ils/modules/documents/jsonschemas/documents/document_contribution_role-v0.0.1.json b/rero_ils/modules/documents/jsonschemas/documents/document_contribution_role-v0.0.1.json index 6d0251fbe7..e5ec090096 100644 --- a/rero_ils/modules/documents/jsonschemas/documents/document_contribution_role-v0.0.1.json +++ b/rero_ils/modules/documents/jsonschemas/documents/document_contribution_role-v0.0.1.json @@ -162,7 +162,7 @@ }, "widget": { "formlyConfig": { - "type": "select", + "type": "multi-select", "props": { "filter": true, "sort": true, diff --git a/rero_ils/modules/documents/jsonschemas/documents/document_intended_audience-v0.0.1.json b/rero_ils/modules/documents/jsonschemas/documents/document_intended_audience-v0.0.1.json index 6b7e834aba..17c98092d7 100644 --- a/rero_ils/modules/documents/jsonschemas/documents/document_intended_audience-v0.0.1.json +++ b/rero_ils/modules/documents/jsonschemas/documents/document_intended_audience-v0.0.1.json @@ -69,7 +69,7 @@ ], "widget": { "formlyConfig": { - "type": "select", + "type": "multi-select", "props": { "sort": true, "filter": true, diff --git a/rero_ils/modules/entities/remote_entities/jsonschemas/remote_entities/remote_entity-v0.0.1.json b/rero_ils/modules/entities/remote_entities/jsonschemas/remote_entities/remote_entity-v0.0.1.json index 62b23f9647..7bfc9bca3c 100644 --- a/rero_ils/modules/entities/remote_entities/jsonschemas/remote_entities/remote_entity-v0.0.1.json +++ b/rero_ils/modules/entities/remote_entities/jsonschemas/remote_entities/remote_entity-v0.0.1.json @@ -55,7 +55,7 @@ ], "widget": { "formlyConfig": { - "type": "select", + "type": "multi-select", "props": { "sort": true, "options": [ diff --git a/rero_ils/modules/locations/jsonschemas/locations/location-v0.0.1.json b/rero_ils/modules/locations/jsonschemas/locations/location-v0.0.1.json index 7cc7b1ed88..6151034792 100644 --- a/rero_ils/modules/locations/jsonschemas/locations/location-v0.0.1.json +++ b/rero_ils/modules/locations/jsonschemas/locations/location-v0.0.1.json @@ -236,16 +236,6 @@ "formlyConfig": { "expressions": { "hide": "field?.parent?.model?.allow_request === false" - }, - "wrappers": [ - "toggle-switch" - ], - "props": { - "hideLabel": true, - "toggle-switch": { - "label": "Restrict pickup to", - "description": "If enabled, restrict pickup to specific pickup locations." - } } } } diff --git a/rero_ils/modules/patron_types/jsonschemas/patron_types/patron_type-v0.0.1.json b/rero_ils/modules/patron_types/jsonschemas/patron_types/patron_type-v0.0.1.json index 732f08ac47..cacb506bf5 100644 --- a/rero_ils/modules/patron_types/jsonschemas/patron_types/patron_type-v0.0.1.json +++ b/rero_ils/modules/patron_types/jsonschemas/patron_types/patron_type-v0.0.1.json @@ -80,24 +80,10 @@ } }, "subscription_amount": { - "title": "Amount", + "title": "Yearly subscription", "description": "Yearly amount due by patrons linked to this patron type.", "type": "number", - "exclusiveMinimum": 0, - "widget": { - "formlyConfig": { - "wrappers": [ - "toggle-switch", - "form-field" - ], - "props": { - "toggle-switch": { - "label": "Yearly subscription", - "description": "Activation of subscription fees for patrons linked to this type." - } - } - } - } + "exclusiveMinimum": 0 }, "limits": { "title": "Limits", @@ -110,6 +96,7 @@ ], "properties": { "checkout_limits": { + "title": "Limit by checkouts", "type": "object", "additionalProperties": false, "required": [ @@ -186,27 +173,12 @@ } } } - }, - "widget": { - "formlyConfig": { - "wrappers": [ - "toggle-switch", - "form-field" - ], - "props": { - "toggle-switch": { - "label": "Limit by checkouts" - } - } - } } }, "fee_amount_limits": { + "title": "Limit by overdue fee amount", "type": "object", "additionalProperties": false, - "required": [ - "default_value" - ], "properties": { "default_value": { "title": "Fee amount", @@ -214,27 +186,12 @@ "type": "number", "minimum": 0 } - }, - "widget": { - "formlyConfig": { - "wrappers": [ - "toggle-switch", - "form-field" - ], - "props": { - "toggle-switch": { - "label": "Limit by overdue fee amount" - } - } - } } }, "overdue_items_limits": { + "title": "Limit by overdue items", "type": "object", "additionalProperties": false, - "required": [ - "default_value" - ], "properties": { "default_value": { "title": "Number of overdue items", @@ -242,19 +199,6 @@ "type": "number", "minimum": 1 } - }, - "widget": { - "formlyConfig": { - "wrappers": [ - "toggle-switch", - "form-field" - ], - "props": { - "toggle-switch": { - "label": "Limit by overdue items" - } - } - } } }, "unpaid_subscription": { diff --git a/rero_ils/modules/permissions.py b/rero_ils/modules/permissions.py index c2e189ca4e..f1c2770047 100644 --- a/rero_ils/modules/permissions.py +++ b/rero_ils/modules/permissions.py @@ -42,6 +42,8 @@ from rero_ils.modules.patrons.api import current_librarian, current_patrons from rero_ils.modules.utils import get_record_class_and_permissions_from_route +from .acquisition.acq_orders.api import AcqOrder + # SPECIFIC ACTIONS ============================================================ # Some actions are not related to a specific resource. For this case, we # can create a specific action in this root permission module. @@ -481,3 +483,35 @@ def excludes(self, record=None, **kwargs): if self.is_rollovered(record): return [any_user] return [] + + +class DisallowedByOrderStatus(Generator): + """Disallow if the record is considerate roll-overed.""" + + def __init__(self, record_cls, allowed_statuses): + """Constructor. + + :param record_cls: the record class to build a resource if record is + received is only dict/data. + :param callback: the function to use to know if the resource is + rollovered. This function should return a boolean value. By default + the ``is_active`` record property will be returned if exists ; + otherwise True. + """ + self.record_cls = record_cls + self.allowed_statuses = allowed_statuses + + def excludes(self, record=None, **kwargs): + """Disallow operation check. + + :param record; the record to check. + :param kwargs: extra named arguments. + :returns: a list of Needs to disable access. + """ + if record: + if not isinstance(record, self.record_cls): + record = self.record_cls(record) + if order_status := AcqOrder.get_status_by_pid(record.order_pid): + if not order_status in self.allowed_statuses: + return [any_user] + return [] diff --git a/rero_ils/modules/stats_cfg/jsonschemas/stats_cfg/stat_cfg-v0.0.1.json b/rero_ils/modules/stats_cfg/jsonschemas/stats_cfg/stat_cfg-v0.0.1.json index 5405588a5e..5c835dd2ec 100644 --- a/rero_ils/modules/stats_cfg/jsonschemas/stats_cfg/stat_cfg-v0.0.1.json +++ b/rero_ils/modules/stats_cfg/jsonschemas/stats_cfg/stat_cfg-v0.0.1.json @@ -205,7 +205,6 @@ }, { "items": { - "additionalProperties": false, "title": "distributions", "type": "string", "enum": [ @@ -217,7 +216,7 @@ }, "widget": { "formlyConfig": { - "type": "select", + "type": "multi-select", "props": { "sort": false, "options": [ @@ -277,7 +276,6 @@ }, { "items": { - "additionalProperties": false, "title": "distributions", "type": "string", "enum": [ @@ -288,7 +286,7 @@ }, "widget": { "formlyConfig": { - "type": "select", + "type": "multi-select", "props": { "sort": false, "options": [ @@ -344,7 +342,6 @@ }, { "items": { - "additionalProperties": false, "title": "distributions", "type": "string", "enum": [ @@ -359,7 +356,7 @@ }, "widget": { "formlyConfig": { - "type": "select", + "type": "multi-select", "props": { "sort": false, "options": [ @@ -432,7 +429,6 @@ }, { "items": { - "additionalProperties": false, "title": "distributions", "type": "string", "enum": [ @@ -444,7 +440,7 @@ }, "widget": { "formlyConfig": { - "type": "select", + "type": "multi-select", "props": { "sort": false, "options": [ @@ -551,7 +547,7 @@ }, "widget": { "formlyConfig": { - "type": "select", + "type": "multi-select", "props": { "sort": false, "options": [ @@ -907,7 +903,7 @@ }, "widget": { "formlyConfig": { - "type": "select", + "type": "multi-select", "props": { "sort": false, "options": [ @@ -982,7 +978,7 @@ }, "widget": { "formlyConfig": { - "type": "select", + "type": "multi-select", "props": { "sort": false, "options": [ @@ -1054,7 +1050,7 @@ }, "widget": { "formlyConfig": { - "type": "select", + "type": "multi-select", "props": { "sort": false, "options": [ diff --git a/tests/api/acq_order_lines/test_acq_order_lines_permissions.py b/tests/api/acq_order_lines/test_acq_order_lines_permissions.py index de02e03098..92e8beafd1 100644 --- a/tests/api/acq_order_lines/test_acq_order_lines_permissions.py +++ b/tests/api/acq_order_lines/test_acq_order_lines_permissions.py @@ -25,6 +25,7 @@ from rero_ils.modules.acquisition.acq_order_lines.permissions import ( AcqOrderLinePermissionPolicy, ) +from rero_ils.modules.acquisition.acq_orders.models import AcqOrderStatus def test_order_lines_permissions( @@ -158,3 +159,82 @@ def test_order_lines_permissions( }, acq_order_line_fiction_martigny, ) + + with mock.patch( + "rero_ils.modules.acquisition.acq_orders.api.AcqOrder.get_status_by_pid", + mock.MagicMock(return_value=AcqOrderStatus.CANCELLED), + ): + check_permission( + AcqOrderLinePermissionPolicy, + { + "search": True, + "read": True, + "create": True, + "update": True, + "delete": True, + }, + acq_order_line_fiction_martigny, + ) + + with mock.patch( + "rero_ils.modules.acquisition.acq_orders.api.AcqOrder.get_status_by_pid", + mock.MagicMock(return_value=AcqOrderStatus.PENDING), + ): + check_permission( + AcqOrderLinePermissionPolicy, + { + "search": True, + "read": True, + "create": True, + "update": True, + "delete": True, + }, + acq_order_line_fiction_martigny, + ) + + with mock.patch( + "rero_ils.modules.acquisition.acq_orders.api.AcqOrder.get_status_by_pid", + mock.MagicMock(return_value=AcqOrderStatus.ORDERED), + ): + check_permission( + AcqOrderLinePermissionPolicy, + { + "search": True, + "read": True, + "create": True, + "update": True, + "delete": False, + }, + acq_order_line_fiction_martigny, + ) + + with mock.patch( + "rero_ils.modules.acquisition.acq_orders.api.AcqOrder.get_status_by_pid", + mock.MagicMock(return_value=AcqOrderStatus.PARTIALLY_RECEIVED), + ): + check_permission( + AcqOrderLinePermissionPolicy, + { + "search": True, + "read": True, + "create": True, + "update": True, + "delete": False, + }, + acq_order_line_fiction_martigny, + ) + with mock.patch( + "rero_ils.modules.acquisition.acq_orders.api.AcqOrder.get_status_by_pid", + mock.MagicMock(return_value=AcqOrderStatus.RECEIVED), + ): + check_permission( + AcqOrderLinePermissionPolicy, + { + "search": True, + "read": True, + "create": True, + "update": False, + "delete": False, + }, + acq_order_line_fiction_martigny, + ) diff --git a/tests/api/acq_orders/test_acq_orders_rest.py b/tests/api/acq_orders/test_acq_orders_rest.py index ec2b84e1cd..9b37ccecf2 100644 --- a/tests/api/acq_orders/test_acq_orders_rest.py +++ b/tests/api/acq_orders/test_acq_orders_rest.py @@ -112,7 +112,6 @@ def test_acq_orders_post_put_delete( } assert data["metadata"].pop("status") == AcqOrderStatus.PENDING - assert not data["metadata"].pop("order_date", None) acq_order_fiction_saxon_data["pid"] = data["metadata"]["pid"] assert data["metadata"] == acq_order_fiction_saxon_data @@ -128,7 +127,6 @@ def test_acq_orders_post_put_delete( "expenditure": {"total_amount": 0, "quantity": 0}, } assert data["metadata"].pop("status") == AcqOrderStatus.PENDING - assert not data["metadata"].pop("order_date", None) assert acq_order_fiction_saxon_data == data["metadata"] # Update record/PUT @@ -221,7 +219,6 @@ def test_acq_order_history_api( data = { "vendor": {"$ref": get_ref_for_pid("vndr", vendor_martigny.pid)}, "library": {"$ref": get_ref_for_pid("lib", lib_martigny.pid)}, - "type": "monograph", } acor1 = _make_resource(client, "acor", data) data["previousVersion"] = {"$ref": get_ref_for_pid("acor", acor1.pid)} diff --git a/tests/api/acq_orders/test_acq_orders_serializers.py b/tests/api/acq_orders/test_acq_orders_serializers.py index 3a56cd3a85..8cc0dac2bf 100644 --- a/tests/api/acq_orders/test_acq_orders_serializers.py +++ b/tests/api/acq_orders/test_acq_orders_serializers.py @@ -47,7 +47,7 @@ def test_csv_serializer( assert data assert ( '"order_pid","order_reference","order_date","order_staff_note",' - '"order_vendor_note","order_type","order_status","vendor_name",' + '"order_vendor_note","order_status","vendor_name",' '"document_pid","document_creator","document_title",' '"document_publisher","document_publication_year",' '"document_edition_statement","document_series_statement",' diff --git a/tests/api/acq_orders/test_acq_orders_views.py b/tests/api/acq_orders/test_acq_orders_views.py index 48484b1ee5..b27d5070dd 100644 --- a/tests/api/acq_orders/test_acq_orders_views.py +++ b/tests/api/acq_orders/test_acq_orders_views.py @@ -152,12 +152,10 @@ def test_send_order( l2 = AcqOrderLine.get_record_by_pid(acq_order_line2_fiction_martigny.pid) l3 = AcqOrderLine.get_record_by_pid(acq_order_line3_fiction_martigny.pid) assert l1.status == AcqOrderLineStatus.ORDERED - assert l1.order_date assert l2.status == AcqOrderLineStatus.ORDERED - assert l2.order_date assert l3.status == AcqOrderLineStatus.CANCELLED - assert not l3.order_date assert acor.status == AcqOrderStatus.ORDERED + assert acor.get("order_date") # ensure that created notification is well constructed from the associated # order and vendor diff --git a/tests/api/acq_receipt_lines/test_acq_receipt_lines_rest.py b/tests/api/acq_receipt_lines/test_acq_receipt_lines_rest.py index 804b195f78..675145063a 100644 --- a/tests/api/acq_receipt_lines/test_acq_receipt_lines_rest.py +++ b/tests/api/acq_receipt_lines/test_acq_receipt_lines_rest.py @@ -19,57 +19,55 @@ """Tests REST API acquisition receipt lines.""" import json -from copy import deepcopy import mock from flask import url_for -from invenio_accounts.testutils import login_user_via_session from utils import VerifyRecordPermissionPatch, get_json, postdata, to_relative_url from rero_ils.modules.acquisition.acq_receipt_lines.models import AcqReceiptLineNoteType -@mock.patch( - "invenio_records_rest.views.verify_record_permission", - mock.MagicMock(return_value=VerifyRecordPermissionPatch), -) def test_acq_receipt_lines_get(client, acq_receipt_line_1_fiction_martigny): """Test record retrieval.""" - item_url = url_for("invenio_records_rest.acrl_item", pid_value="acrl1") - acq_receipt_line = deepcopy(acq_receipt_line_1_fiction_martigny) - res = client.get(item_url) - assert res.status_code == 200 + with mock.patch( + "invenio_records_rest.views.verify_record_permission", + mock.MagicMock(return_value=VerifyRecordPermissionPatch), + ): + item_url = url_for("invenio_records_rest.acrl_item", pid_value="acrl1") + acq_receipt_line = acq_receipt_line_1_fiction_martigny + res = client.get(item_url) + assert res.status_code == 200 - assert res.headers["ETag"] == f'"{acq_receipt_line.revision_id}"' + assert res.headers["ETag"] == f'"{acq_receipt_line.revision_id}"' - data = get_json(res) - assert acq_receipt_line.dumps() == data["metadata"] + data = get_json(res) + assert acq_receipt_line.dumps() == data["metadata"] - # Check metadata - for k in ["created", "updated", "metadata", "links"]: - assert k in data + # Check metadata + for k in ["created", "updated", "metadata", "links"]: + assert k in data - # Check self links - res = client.get(to_relative_url(data["links"]["self"])) - assert res.status_code == 200 - assert data == get_json(res) - assert acq_receipt_line.dumps() == data["metadata"] + # Check self links + res = client.get(to_relative_url(data["links"]["self"])) + assert res.status_code == 200 + assert data == get_json(res) + assert acq_receipt_line.dumps() == data["metadata"] - list_url = url_for("invenio_records_rest.acrl_list", pid="acrl1") - res = client.get(list_url) - assert res.status_code == 200 - data = get_json(res) + list_url = url_for("invenio_records_rest.acrl_list", pid="acrl1") + res = client.get(list_url) + assert res.status_code == 200 + data = get_json(res) - metadata = data["hits"]["hits"][0]["metadata"] + metadata = data["hits"]["hits"][0]["metadata"] - total_amount = metadata["total_amount"] - assert total_amount == 1000.0 + total_amount = metadata["total_amount"] + assert total_amount == 1000.0 - # remove dynamically added fields - del metadata["acq_account"] - del metadata["total_amount"] - del metadata["document"] - assert data["hits"]["hits"][0]["metadata"] == acq_receipt_line.replace_refs() + # remove dynamically added fields + del metadata["acq_account"] + del metadata["total_amount"] + del metadata["document"] + assert data["hits"]["hits"][0]["metadata"] == acq_receipt_line.replace_refs() @mock.patch( @@ -142,36 +140,3 @@ def test_acq_receipt_lines_can_delete( can, reasons = acq_receipt_line_1_fiction_martigny.can_delete assert can assert "links" not in reasons - - -def test_filtered_acq_receipt_lines_get( - client, - librarian_martigny, - acq_receipt_line_1_fiction_martigny, - acq_receipt_line_2_fiction_martigny, - librarian_sion, - acq_receipt_line_fiction_sion, -): - """Test acq receipt lines filter by organisation.""" - list_url = url_for("invenio_records_rest.acrl_list") - - res = client.get(list_url) - assert res.status_code == 401 - - # Martigny - login_user_via_session(client, librarian_martigny.user) - list_url = url_for("invenio_records_rest.acrl_list") - - res = client.get(list_url) - assert res.status_code == 200 - data = get_json(res) - assert data["hits"]["total"]["value"] == 3 - - # Sion - login_user_via_session(client, librarian_sion.user) - list_url = url_for("invenio_records_rest.acrl_list") - - res = client.get(list_url) - assert res.status_code == 200 - data = get_json(res) - assert data["hits"]["total"]["value"] == 1 diff --git a/tests/api/acq_receipt_lines/test_acq_receipt_lines_rest_filtered.py b/tests/api/acq_receipt_lines/test_acq_receipt_lines_rest_filtered.py new file mode 100644 index 0000000000..4b3d634aba --- /dev/null +++ b/tests/api/acq_receipt_lines/test_acq_receipt_lines_rest_filtered.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +# +# RERO ILS +# Copyright (C) 2021 RERO +# Copyright (C) 2021 UCLouvain +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +"""Tests REST API acquisition receipt lines.""" + + +from flask import url_for +from invenio_accounts.testutils import login_user_via_session +from utils import get_json + + +def test_filtered_acq_receipt_lines_get( + client, + librarian_martigny, + acq_order_line_fiction_martigny, + acq_order_line2_fiction_martigny, + acq_receipt_line_1_fiction_martigny, + acq_receipt_line_2_fiction_martigny, + librarian_sion, + acq_receipt_line_fiction_sion, +): + """Test acq receipt lines filter by organisation.""" + list_url = url_for("invenio_records_rest.acrl_list") + + res = client.get(list_url) + assert res.status_code == 401 + + # Martigny + login_user_via_session(client, librarian_martigny.user) + list_url = url_for("invenio_records_rest.acrl_list") + + res = client.get(list_url) + assert res.status_code == 200 + data = get_json(res) + assert data["hits"]["total"]["value"] == 2 + + # Sion + login_user_via_session(client, librarian_sion.user) + list_url = url_for("invenio_records_rest.acrl_list") + + res = client.get(list_url) + assert res.status_code == 200 + data = get_json(res) + assert data["hits"]["total"]["value"] == 1 diff --git a/tests/api/acq_receipts/test_acq_receipts_permissions.py b/tests/api/acq_receipts/test_acq_receipts_permissions.py index d1831fc316..8fd90bc9cd 100644 --- a/tests/api/acq_receipts/test_acq_receipts_permissions.py +++ b/tests/api/acq_receipts/test_acq_receipts_permissions.py @@ -22,6 +22,7 @@ from flask_security import login_user from utils import check_permission +from rero_ils.modules.acquisition.acq_orders.models import AcqOrderStatus from rero_ils.modules.acquisition.acq_receipts.permissions import ( AcqReceiptPermissionPolicy, ) @@ -37,6 +38,8 @@ def test_receipts_permissions( acq_receipt_fiction_sion, acq_receipt_fiction_saxon, acq_receipt_fiction_martigny, + acq_receipt_line_1_fiction_martigny, + acq_receipt_line_fiction_saxon, ): """Test receipt permissions class.""" @@ -155,3 +158,79 @@ def test_receipts_permissions( }, acq_receipt_fiction_martigny, ) + + with mock.patch( + "rero_ils.modules.acquisition.acq_orders.api.AcqOrder.get_status_by_pid", + mock.MagicMock(return_value=AcqOrderStatus.CANCELLED), + ): + check_permission( + AcqReceiptPermissionPolicy, + { + "search": True, + "read": True, + "create": True, + "update": False, + "delete": False, + }, + acq_receipt_fiction_martigny, + ) + with mock.patch( + "rero_ils.modules.acquisition.acq_orders.api.AcqOrder.get_status_by_pid", + mock.MagicMock(return_value=AcqOrderStatus.PENDING), + ): + check_permission( + AcqReceiptPermissionPolicy, + { + "search": True, + "read": True, + "create": True, + "update": False, + "delete": False, + }, + acq_receipt_fiction_martigny, + ) + with mock.patch( + "rero_ils.modules.acquisition.acq_orders.api.AcqOrder.get_status_by_pid", + mock.MagicMock(return_value=AcqOrderStatus.ORDERED), + ): + check_permission( + AcqReceiptPermissionPolicy, + { + "search": True, + "read": True, + "create": True, + "update": False, + "delete": False, + }, + acq_receipt_fiction_martigny, + ) + with mock.patch( + "rero_ils.modules.acquisition.acq_orders.api.AcqOrder.get_status_by_pid", + mock.MagicMock(return_value=AcqOrderStatus.PARTIALLY_RECEIVED), + ): + check_permission( + AcqReceiptPermissionPolicy, + { + "search": True, + "read": True, + "create": True, + "update": True, + "delete": True, + }, + acq_receipt_fiction_martigny, + ) + with mock.patch( + "rero_ils.modules.acquisition.acq_orders.api.AcqOrder.get_status_by_pid", + mock.MagicMock(return_value=AcqOrderStatus.RECEIVED), + ): + check_permission( + AcqReceiptPermissionPolicy, + { + "search": True, + "read": True, + "create": True, + "update": False, + "delete": True, + }, + acq_receipt_fiction_martigny, + ) diff --git a/tests/api/acq_receipts/test_acq_receipts_rest.py b/tests/api/acq_receipts/test_acq_receipts_rest.py index ada66126a8..06977282f1 100644 --- a/tests/api/acq_receipts/test_acq_receipts_rest.py +++ b/tests/api/acq_receipts/test_acq_receipts_rest.py @@ -96,26 +96,18 @@ def test_acq_receipts_post_put_delete( # Update record/PUT data = acq_receipt_fiction_saxon - data["exchange_rate"] = 1.01 res = client.put(item_url, data=json.dumps(data), headers=json_header) assert res.status_code == 200 # Check that the returned record matches the given data data = get_json(res) - assert data["metadata"]["exchange_rate"] == 1.01 res = client.get(item_url) assert res.status_code == 200 - data = get_json(res) - assert data["metadata"]["exchange_rate"] == 1.01 - res = client.get(list_url) assert res.status_code == 200 - data = get_json(res)["hits"]["hits"][0] - assert data["metadata"]["exchange_rate"] == 1.01 - # Delete record/DELETE res = client.delete(item_url) assert res.status_code == 204 diff --git a/tests/api/acquisition/test_acquisition_reception_workflow.py b/tests/api/acquisition/test_acquisition_reception_workflow.py index ee032a910c..004f5770e7 100644 --- a/tests/api/acquisition/test_acquisition_reception_workflow.py +++ b/tests/api/acquisition/test_acquisition_reception_workflow.py @@ -163,7 +163,6 @@ def assert_account_data(accounts): data = { "vendor": {"$ref": get_ref_for_pid("vndr", vendor_martigny.pid)}, "library": {"$ref": get_ref_for_pid("lib", lib_martigny.pid)}, - "type": "monograph", } order = _make_resource(client, "acor", data) assert order["reference"] == f"ORDER-{order.pid}" @@ -272,7 +271,7 @@ def assert_account_data(accounts): assert order.status == AcqOrderStatus.PENDING # TODO: fix links to me for the order resource, this should fail assert order.can_delete - assert not order.order_date + assert not order.get("order_date") assert order.item_quantity == 24 assert order.item_received_quantity == 0 @@ -391,10 +390,6 @@ def assert_account_data(accounts): ]: line = AcqOrderLine.get_record_by_pid(order_line.get("line").pid) assert line.status == order_line.get("status") - if line.status == AcqOrderLineStatus.CANCELLED: - assert not line.order_date - else: - assert line.order_date # check order order = AcqOrder.get_record_by_pid(order.pid) assert order.status == AcqOrderStatus.ORDERED @@ -419,7 +414,6 @@ def assert_account_data(accounts): ref_acc_serial = get_ref_for_pid("acac", m_serials_acc.pid) data = { "acq_order": {"$ref": get_ref_for_pid("acor", order.pid)}, - "exchange_rate": 1, "amount_adjustments": [ { "label": "handling fees", @@ -533,7 +527,6 @@ def assert_account_data(accounts): # except `order_line_5` should have the RECEIVED STATUS # * complete the order reception to receive the `order_line_5` data = { - "exchange_rate": 1, "acq_order": {"$ref": get_ref_for_pid("acor", order.pid)}, "library": {"$ref": get_ref_for_pid("lib", lib_martigny.pid)}, "organisation": {"$ref": get_ref_for_pid("org", org_martigny.pid)}, diff --git a/tests/api/acquisition/test_acquisition_scenarios.py b/tests/api/acquisition/test_acquisition_scenarios.py index 9af71e7847..85aee10ca9 100644 --- a/tests/api/acquisition/test_acquisition_scenarios.py +++ b/tests/api/acquisition/test_acquisition_scenarios.py @@ -452,7 +452,6 @@ def test_acquisition_order( order_data = { "vendor": {"$ref": get_ref_for_pid("vndr", vendor_martigny.pid)}, "library": {"$ref": get_ref_for_pid("lib", lib_martigny.pid)}, - "type": "monograph", } order = _make_resource(client, "acor", order_data) assert order["reference"] == f"ORDER-{order.pid}" @@ -622,7 +621,6 @@ def test_acquisition_order_line_account_changes( { "vendor": {"$ref": get_ref_for_pid("vndr", vendor_martigny.pid)}, "library": {"$ref": get_ref_for_pid("lib", lib_martigny.pid)}, - "type": "monograph", }, ) order_line = _make_resource( diff --git a/tests/api/acquisition/test_acquisition_serializers.py b/tests/api/acquisition/test_acquisition_serializers.py index eda0d049b3..09b9d5a79b 100644 --- a/tests/api/acquisition/test_acquisition_serializers.py +++ b/tests/api/acquisition/test_acquisition_serializers.py @@ -50,7 +50,6 @@ def test_acquisition_orders_serializers( "vendor": {"$ref": get_ref_for_pid("vndr", vendor_martigny.pid)}, "library": {"$ref": get_ref_for_pid("lib", lib_martigny.pid)}, "reference": "ORDER#1", - "type": "monograph", } order = _make_resource(client, "acor", order_data) order.reindex() diff --git a/tests/api/test_serializers.py b/tests/api/test_serializers.py index c7cbabd95e..1f5b10a403 100644 --- a/tests/api/test_serializers.py +++ b/tests/api/test_serializers.py @@ -307,8 +307,10 @@ def test_acq_orders_serializers( def test_acq_receipts_serializers( client, rero_json_header, - acq_order_fiction_martigny, acq_account_fiction_martigny, + acq_order_fiction_martigny, + acq_order_line_fiction_martigny, + acq_order_line2_fiction_martigny, acq_receipt_fiction_martigny, acq_receipt_line_1_fiction_martigny, acq_receipt_line_2_fiction_martigny, diff --git a/tests/data/acquisition.json b/tests/data/acquisition.json index 0c6c790f7d..644cf49794 100644 --- a/tests/data/acquisition.json +++ b/tests/data/acquisition.json @@ -262,8 +262,7 @@ "library": { "$ref": "https://bib.rero.ch/api/libraries/lib1" }, - "reference": "OR-1", - "type": "monograph" + "reference": "OR-1" }, "acor2": { "$schema": "https://bib.rero.ch/schemas/acq_orders/acq_order-v0.0.1.json", @@ -274,8 +273,7 @@ "library": { "$ref": "https://bib.rero.ch/api/libraries/lib2" }, - "reference": "OR-2", - "type": "monograph" + "reference": "OR-2" }, "acor3": { "$schema": "https://bib.rero.ch/schemas/acq_orders/acq_order-v0.0.1.json", @@ -286,8 +284,7 @@ "library": { "$ref": "https://bib.rero.ch/api/libraries/lib4" }, - "reference": "OR-3", - "type": "monograph" + "reference": "OR-3" }, "acol1": { "$schema": "https://bib.rero.ch/schemas/acq_order_lines/acq_order_line-v0.0.1.json", @@ -383,7 +380,6 @@ "acq_order": { "$ref": "https://bib.rero.ch/api/acq_orders/acor1" }, - "exchange_rate": 1, "amount_adjustments": [ { "label": "handling fees", @@ -455,7 +451,6 @@ "acq_order": { "$ref": "https://bib.rero.ch/api/acq_orders/acor2" }, - "exchange_rate": 1, "amount_adjustments": [ { "label": "handling fees", @@ -509,7 +504,6 @@ "acq_order": { "$ref": "https://bib.rero.ch/api/acq_orders/acor3" }, - "exchange_rate": 1, "amount_adjustments": [ { "label": "handling fees", diff --git a/tests/data/data.json b/tests/data/data.json index 22f9258f61..06c7cf8c47 100644 --- a/tests/data/data.json +++ b/tests/data/data.json @@ -658,6 +658,30 @@ "communication_language": "fre", "rollover_settings": { "account_transfer": "rollover_no_transfer" + }, + "acquisition_settings": { + "shipping_informations": { + "name": "sion shipping dept", + "email": "reroilstest+sionshipping@gmail.com", + "phone": "+447763324594", + "address": { + "street": "Av. de la gare 134", + "city": "Sion", + "zip_code": "1950", + "country": "sz" + }, + "extra": "VAT number: 123456-78-90" + }, + "billing_informations": { + "name": "sion billing dept", + "email": "reroilstest+sionshipping@gmail.com", + "address": { + "street": "Place de la planta 12", + "city": "Sion", + "zip_code": "1950", + "country": "sz" + } + } } }, "lib5": { diff --git a/tests/fixtures/acquisition.py b/tests/fixtures/acquisition.py index 552ad033f9..9ff30c2177 100644 --- a/tests/fixtures/acquisition.py +++ b/tests/fixtures/acquisition.py @@ -16,10 +16,9 @@ # along with this program. If not, see . """Common pytest fixtures and plugins.""" - - from copy import deepcopy +import mock import pytest from api.acquisition.acq_utils import _make_resource @@ -33,6 +32,7 @@ AcqOrderLinesSearch, ) from rero_ils.modules.acquisition.acq_orders.api import AcqOrder, AcqOrdersSearch +from rero_ils.modules.acquisition.acq_orders.models import AcqOrderStatus from rero_ils.modules.acquisition.acq_receipt_lines.api import ( AcqReceiptLine, AcqReceiptLinesSearch, @@ -314,7 +314,7 @@ def acq_account_fiction_sion_data(acquisition): @pytest.fixture(scope="module") def acq_account_fiction_sion( - app, lib_saxon, acq_account_fiction_sion_data, budget_2020_sion + app, lib_sion, acq_account_fiction_sion_data, budget_2020_sion ): """Load acq_account lib sion fiction record.""" acac = AcqAccount.create( @@ -410,10 +410,22 @@ def acq_receipt_fiction_martigny( app, lib_martigny, acq_order_fiction_martigny, + acq_order_line_fiction_martigny, acq_receipt_fiction_martigny_data, acq_account_fiction_martigny, ): """Load acq_receipt lib martigny fiction record.""" + if acq_order_fiction_martigny.status == AcqOrderStatus.PENDING: + with mock.patch( + "rero_ils.modules.notifications.dispatcher.Dispatcher.dispatch_notifications", + mock.MagicMock(return_value={"sent": 1}), + ): + acq_order_fiction_martigny.send_order( + [ + {"type": "to", "address": "ils@foo.com"}, + {"type": "reply_to", "address": "admin@foo.com"}, + ] + ) acor = AcqReceipt.create( data=acq_receipt_fiction_martigny_data, delete_pid=False, @@ -427,8 +439,9 @@ def acq_receipt_fiction_martigny( @pytest.fixture(scope="module") def acq_receipt_line_1_fiction_martigny( app, - acq_receipt_fiction_martigny, + acq_order_fiction_martigny, acq_order_line_fiction_martigny, + acq_receipt_fiction_martigny, acq_receipt_line_1_fiction_martigny_data, ): """Load acq_receipt_line_1 lib martigny fiction record.""" @@ -445,8 +458,9 @@ def acq_receipt_line_1_fiction_martigny( @pytest.fixture(scope="module") def acq_receipt_line_2_fiction_martigny( app, - acq_receipt_fiction_martigny, + acq_order_fiction_martigny, acq_order_line2_fiction_martigny, + acq_receipt_fiction_martigny, acq_receipt_line_2_fiction_martigny_data, ): """Load acq_receipt_line_2 lib martigny fiction record.""" @@ -496,10 +510,22 @@ def acq_receipt_fiction_saxon( lib_saxon, vendor_martigny, acq_order_fiction_saxon, + acq_order_line_fiction_saxon, acq_receipt_fiction_saxon_data, acq_account_books_saxon, ): """Load acq_receipt lib saxon fiction record.""" + if acq_order_fiction_saxon.status == AcqOrderStatus.PENDING: + with mock.patch( + "rero_ils.modules.notifications.dispatcher.Dispatcher.dispatch_notifications", + mock.MagicMock(return_value={"sent": 1}), + ): + acq_order_fiction_saxon.send_order( + [ + {"type": "to", "address": "ils@foo.com"}, + {"type": "reply_to", "address": "admin@foo.com"}, + ] + ) acre = AcqReceipt.create( data=acq_receipt_fiction_saxon_data, delete_pid=False, @@ -513,8 +539,9 @@ def acq_receipt_fiction_saxon( @pytest.fixture(scope="module") def acq_receipt_line_fiction_saxon( app, - acq_receipt_fiction_saxon, + acq_order_fiction_saxon, acq_order_line_fiction_saxon, + acq_receipt_fiction_saxon, acq_receipt_line_fiction_saxon_data, ): """Load acq_receipt_line lib saxon fiction record.""" @@ -561,11 +588,23 @@ def acq_receipt_fiction_sion( app, lib_sion, vendor_sion, + acq_account_fiction_sion, acq_order_fiction_sion, + acq_order_line_fiction_sion, acq_receipt_fiction_sion_data, - acq_account_fiction_sion, ): """Load acq_receipt lib sion fiction record.""" + if acq_order_fiction_sion.status == AcqOrderStatus.PENDING: + with mock.patch( + "rero_ils.modules.notifications.dispatcher.Dispatcher.dispatch_notifications", + mock.MagicMock(return_value={"sent": 1}), + ): + acq_order_fiction_sion.send_order( + [ + {"type": "to", "address": "ils@foo.com"}, + {"type": "reply_to", "address": "admin@foo.com"}, + ] + ) acor = AcqReceipt.create( data=acq_receipt_fiction_sion_data, delete_pid=False, @@ -579,8 +618,9 @@ def acq_receipt_fiction_sion( @pytest.fixture(scope="module") def acq_receipt_line_fiction_sion( app, - acq_receipt_fiction_sion, + acq_order_fiction_sion, acq_order_line_fiction_sion, + acq_receipt_fiction_sion, acq_receipt_line_fiction_sion_data, ): """Load acq_receipt_line lib sion fiction record.""" @@ -609,9 +649,9 @@ def acq_order_line_fiction_martigny_data_tmp(acquisition): @pytest.fixture(scope="module") def acq_order_line_fiction_martigny( app, + acq_order_fiction_martigny, acq_account_fiction_martigny, document, - acq_order_fiction_martigny, acq_order_line_fiction_martigny_data, ): """Load acq_order_line lib martigny fiction record.""" @@ -684,6 +724,7 @@ def acq_order_line_fiction_saxon_data(acquisition): @pytest.fixture(scope="module") def acq_order_line_fiction_saxon( app, + document, acq_account_books_saxon, acq_order_fiction_saxon, acq_order_line_fiction_saxon_data, @@ -708,6 +749,7 @@ def acq_order_line_fiction_sion_data(acquisition): @pytest.fixture(scope="module") def acq_order_line_fiction_sion( app, + document, acq_account_fiction_sion, acq_order_fiction_sion, acq_order_line_fiction_sion_data, @@ -890,29 +932,17 @@ def acq_full_structure_a(client, lib_martigny, vendor_martigny, document, org_ma order_10 = _make_resource( client, "acor", - { - "vendor": {"$ref": vendor_ref}, - "library": {"$ref": lib_ref}, - "type": "monograph", - }, + {"vendor": {"$ref": vendor_ref}, "library": {"$ref": lib_ref}}, ) order_20 = _make_resource( client, "acor", - { - "vendor": {"$ref": vendor_ref}, - "library": {"$ref": lib_ref}, - "type": "monograph", - }, + {"vendor": {"$ref": vendor_ref}, "library": {"$ref": lib_ref}}, ) order_30 = _make_resource( client, "acor", - { - "vendor": {"$ref": vendor_ref}, - "library": {"$ref": lib_ref}, - "type": "monograph", - }, + {"vendor": {"$ref": vendor_ref}, "library": {"$ref": lib_ref}}, ) # OrderLines ========================================== orderline_10_1 = _make_resource( @@ -948,13 +978,24 @@ def acq_full_structure_a(client, lib_martigny, vendor_martigny, document, org_ma "amount": 33, }, ) + for order in [order_10, order_20, order_30]: + with mock.patch( + "rero_ils.modules.notifications.dispatcher.Dispatcher.dispatch_notifications", + mock.MagicMock(return_value={"sent": 1}), + ): + order.send_order( + [ + {"type": "to", "address": "ils@foo.com"}, + {"type": "reply_to", "address": "admin@foo.com"}, + ] + ) + # Reception =========================================== reception_10_1 = _make_resource( client, "acre", { "acq_order": {"$ref": get_ref("acor", order_10.pid)}, - "exchange_rate": 1, "amount_adjustments": [ { "label": "handling fees", @@ -970,7 +1011,6 @@ def acq_full_structure_a(client, lib_martigny, vendor_martigny, document, org_ma "acre", { "acq_order": {"$ref": get_ref("acor", order_30.pid)}, - "exchange_rate": 1, "library": {"$ref": lib_ref}, }, ) diff --git a/tests/ui/acq_orders/test_acq_orders_api.py b/tests/ui/acq_orders/test_acq_orders_api.py index b4b3dc6838..4705ab6810 100644 --- a/tests/ui/acq_orders/test_acq_orders_api.py +++ b/tests/ui/acq_orders/test_acq_orders_api.py @@ -59,18 +59,6 @@ def test_order_properties( acol1["is_cancelled"] = False acol1.update(acol1, dbcommit=True, reindex=True) - # ORDER DATE -------------------------------------------------------------- - assert acor.order_date is None - - acol2["order_date"] = yesterday.strftime("%Y-%m-%d") - acol2.update(acol2, dbcommit=True, reindex=True) - assert acor.order_date == yesterday.strftime("%Y-%m-%d") - assert acor.status == AcqOrderStatus.ORDERED - - # reset changes - del acol2["order_date"] - acol2.update(acol2, dbcommit=True, reindex=True) - # NOTES ------------------------------------------------------------------- note_content = "test note content" assert acor.get_note(AcqOrderNoteType.VENDOR) is None diff --git a/tests/ui/acq_receipt_lines/test_acq_receipt_lines_api.py b/tests/ui/acq_receipt_lines/test_acq_receipt_lines_api.py index 2c1a357f6a..7c8dd05ae4 100644 --- a/tests/ui/acq_receipt_lines/test_acq_receipt_lines_api.py +++ b/tests/ui/acq_receipt_lines/test_acq_receipt_lines_api.py @@ -38,7 +38,6 @@ def test_receipt_lines_properties( # ORDER LINE -------------------------------------------------------------- assert acrl1.order_line_pid == acq_order_line_fiction_martigny.pid acol = acq_order_line_fiction_martigny - assert acol.receipt_date.strftime("%Y-%m-%d") == acrl1.get("receipt_date") # NOTE -------------------------------------------------------------------- assert acrl1.get_note(AcqReceiptLineNoteType.STAFF) diff --git a/tests/ui/acq_receipts/test_acq_receipts_api.py b/tests/ui/acq_receipts/test_acq_receipts_api.py index 6b517d6b7b..57d914b33c 100644 --- a/tests/ui/acq_receipts/test_acq_receipts_api.py +++ b/tests/ui/acq_receipts/test_acq_receipts_api.py @@ -24,28 +24,11 @@ from rero_ils.modules.utils import extracted_data_from_ref -def test_receipts_custom_validation( - acq_order_fiction_martigny, - acq_account_fiction_martigny, - acq_receipt_fiction_martigny, - acq_receipt_fiction_martigny_data, -): - """test receipts custom validations.""" - acre1 = acq_receipt_fiction_martigny - # TEST ADJUSTMENT AMOUNT WITH BAD DECIMALS -------------------------------- - acre1["amount_adjustments"][0]["amount"] = 1.000003 - with pytest.raises(ValidationError) as err: - acre1 = acre1.update(acre1, dbcommit=True, reindex=True) - assert "must be multiple of 0.01" in str(err) - - acre1["amount_adjustments"][0]["amount"] = -99999.990 - acre1 = acre1.update(acre1, dbcommit=True, reindex=True) - acre1.update(acq_receipt_fiction_martigny_data, dbcommit=True, reindex=True) - - def test_receipts_properties( - acq_order_fiction_martigny, acq_account_fiction_martigny, + acq_order_fiction_martigny, + acq_order_line_fiction_martigny, + acq_order_line2_fiction_martigny, acq_receipt_fiction_martigny, acq_receipt_line_1_fiction_martigny, acq_receipt_line_2_fiction_martigny, @@ -63,8 +46,7 @@ def test_receipts_properties( assert acre1.order_pid == acq_order_fiction_martigny.pid # NOTE -------------------------------------------------------------------- assert acre1.get_note(AcqReceiptNoteType.STAFF) - # EXCHANGE_RATE ----------------------------------------------------------- - assert acre1.exchange_rate + # AMOUNT ------------------------------------------------------------------ adj_amount = sum(adj.get("amount") for adj in acre1.amount_adjustments) wished_amount = sum([acrl1.total_amount, acrl2.total_amount, adj_amount]) @@ -85,3 +67,22 @@ def test_receipts_properties( assert all(pid in lines_pid for pid in acre1.get_receipt_lines("pids")) assert acre1.get_receipt_lines("count") == 2 + + +def test_receipts_custom_validation( + acq_order_fiction_martigny, + acq_account_fiction_martigny, + acq_receipt_fiction_martigny, + acq_receipt_fiction_martigny_data, +): + """test receipts custom validations.""" + acre1 = acq_receipt_fiction_martigny + # TEST ADJUSTMENT AMOUNT WITH BAD DECIMALS -------------------------------- + acre1["amount_adjustments"][0]["amount"] = 1.000003 + with pytest.raises(ValidationError) as err: + acre1 = acre1.update(acre1, dbcommit=True, reindex=True) + assert "must be multiple of 0.01" in str(err) + + acre1["amount_adjustments"][0]["amount"] = -99999.990 + acre1 = acre1.update(acre1, dbcommit=True, reindex=True) + acre1.update(acq_receipt_fiction_martigny_data, dbcommit=True, reindex=True) diff --git a/tests/ui/acq_receipts/test_acq_receipts_mapping.py b/tests/ui/acq_receipts/test_acq_receipts_mapping.py index e2ea556c50..f3265c67b5 100644 --- a/tests/ui/acq_receipts/test_acq_receipts_mapping.py +++ b/tests/ui/acq_receipts/test_acq_receipts_mapping.py @@ -17,6 +17,7 @@ # along with this program. If not, see . """Acquisition receipt record mapping tests.""" +import mock from utils import get_mapping from rero_ils.modules.acquisition.acq_receipts.api import AcqReceipt, AcqReceiptsSearch @@ -28,6 +29,7 @@ def test_acq_receipts_es_mapping( lib_martigny, vendor_martigny, acq_order_fiction_martigny, + acq_order_line_fiction_martigny, acq_account_fiction_martigny, acq_receipt_fiction_martigny_data, ): @@ -35,6 +37,16 @@ def test_acq_receipts_es_mapping( search = AcqReceiptsSearch() mapping = get_mapping(search.Meta.index) assert mapping + with mock.patch( + "rero_ils.modules.notifications.dispatcher.Dispatcher.dispatch_notifications", + mock.MagicMock(return_value={"sent": 1}), + ): + acq_order_fiction_martigny.send_order( + [ + {"type": "to", "address": "ils@foo.com"}, + {"type": "reply_to", "address": "admin@foo.com"}, + ] + ) receipt = AcqReceipt.create( acq_receipt_fiction_martigny_data, dbcommit=True, reindex=True, delete_pid=True )