From 83c97f409be489e45256082a70e3466adf21128a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Rafael=20Siqueira?= Date: Wed, 27 Aug 2025 22:17:33 -0300 Subject: [PATCH] fix billing --- .../app/events/schedule_reminders.py | 12 ++--- http-api/app/routes/enrollments/__init__.py | 17 ++++--- http-api/app/routes/enrollments/cancel.py | 2 +- http-api/app/routes/lookup/__init__.py | 5 +- http-api/app/rules/enrollment.py | 25 ++++++---- http-api/cli/jsonl2sqlite.py | 6 +-- http-api/template.yaml | 2 +- http-api/uv.lock | 48 ++++++++++++++++++- konviva-events/app/app.py | 4 +- konviva-events/app/enrollment.py | 45 ++++++++++++++--- .../app/events/billing/append_enrollment.py | 6 +-- .../app/events/billing/cancel_enrollment.py | 2 +- 12 files changed, 129 insertions(+), 45 deletions(-) diff --git a/enrollments-events/app/events/schedule_reminders.py b/enrollments-events/app/events/schedule_reminders.py index b80c2b0..76fb156 100644 --- a/enrollments-events/app/events/schedule_reminders.py +++ b/enrollments-events/app/events/schedule_reminders.py @@ -30,9 +30,9 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool: item={ 'id': enrollment_id, 'sk': 'SCHEDULE#REMINDER_NO_ACCESS_AFTER_3_DAYS', - 'name': user.name, - 'email': user.email, - 'course': course.name, + 'name': user['name'], + 'email': user['email'], + 'course': course['name'], 'created_at': now_, 'ttl': ttl(days=3, start_dt=now_), }, @@ -56,9 +56,9 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool: item={ 'id': enrollment_id, 'sk': 'SCHEDULE#REMINDER_ACCESS_PERIOD_BEFORE_30_DAYS', - 'name': user.name, - 'email': user.email, - 'course': course.name, + 'name': user['name'], + 'email': user['email'], + 'course': course['name'], 'created_at': now_, 'ttl': ttl(start_dt=now_, days=course.access_period - 30), }, diff --git a/http-api/app/routes/enrollments/__init__.py b/http-api/app/routes/enrollments/__init__.py index b4ea344..6014fc6 100644 --- a/http-api/app/routes/enrollments/__init__.py +++ b/http-api/app/routes/enrollments/__init__.py @@ -39,7 +39,7 @@ def get_enrollments(): tenant: Tenant = router.context['tenant'] event = router.current_event query = parse.unquote(event.get_query_string_value('q', '')) - sort = event.get_query_string_value('sort', 'create_date:desc') + sort = event.get_query_string_value('sort', 'created_at:desc') page = int(event.get_query_string_value('page', '1')) hits_per_page = int(event.get_query_string_value('hitsPerPage', '25')) filter_ = meili.parse(event.get_query_string_value('filter', '')) @@ -70,15 +70,14 @@ def get_enrollment(id: str): return enrollment_layer.collection.get_items( TransactKey(id) + SortKey('0') - + SortKey('started_date') - + SortKey('finished_date') - + SortKey('failed_date') - + SortKey('canceled_date') - + SortKey('archived_date') - + SortKey('cancel_policy') + + SortKey('STARTED', rename_key='started_at', path_spec='started_at') + + SortKey('COMPLETED', rename_key='completed_at', path_spec='completed_at') + + SortKey('FAILED', rename_key='failed_at', path_spec='failed_at') + + SortKey('CANCELED', rename_key='canceled') + + SortKey('ARCHIVED', rename_key='archived_at', path_spec='archived_at') + + SortKey('CANCEL_POLICY', rename_key='cancel_policy') + + SortKey('LOCK', rename_key='lock') + SortKey('parent_vacancy', path_spec='vacancy') - + SortKey('lock') + SortKey('author') + SortKey('tenant') - + SortKey('cert') ) diff --git a/http-api/app/routes/enrollments/cancel.py b/http-api/app/routes/enrollments/cancel.py index cd4ecaf..f6f9b1c 100644 --- a/http-api/app/routes/enrollments/cancel.py +++ b/http-api/app/routes/enrollments/cancel.py @@ -41,7 +41,7 @@ def cancel(id: str, payload: Cancel): set_status_as_canceled( id, lock_hash=payload.lock_hash, - author={ + created_by={ 'id': user.id, 'name': user.name, }, diff --git a/http-api/app/routes/lookup/__init__.py b/http-api/app/routes/lookup/__init__.py index 6cf1805..489a4db 100644 --- a/http-api/app/routes/lookup/__init__.py +++ b/http-api/app/routes/lookup/__init__.py @@ -17,7 +17,10 @@ def lookup(username: str): r = meili_client.index(USER_TABLE).search(f'"{username}"') if user := glom(r, 'hits.0', default=None): - return pick(('id', 'name', 'email', 'cognito__sub'), user) + cognito_sub = user.get('cognito:sub') + return {'cognito__sub': cognito_sub} | pick( + ('id', 'name', 'email', 'cognito__sub'), user + ) return JSONResponse( body={'message': 'User not found.'}, diff --git a/http-api/app/rules/enrollment.py b/http-api/app/rules/enrollment.py index ad17998..2965cf1 100644 --- a/http-api/app/rules/enrollment.py +++ b/http-api/app/rules/enrollment.py @@ -211,7 +211,7 @@ def set_status_as_canceled( id: str, *, lock_hash: str | None = None, - author: Author, + created_by: Author, course: Course | None = None, vacancy_key: KeyPair | None = None, persistence_layer: DynamoDBPersistenceLayer, @@ -235,25 +235,30 @@ def set_status_as_canceled( transact.put( item={ 'id': id, - 'sk': 'canceled', - 'author': author, + 'sk': 'CANCELED', + 'canceled_by': created_by, 'canceled_at': now_, }, ) transact.delete( - key=KeyPair(id, 'cancel_policy'), + key=KeyPair(id, 'CANCEL_POLICY'), cond_expr='attribute_exists(sk)', ) # Remove schedules lifecycle events, referencies and locks - transact.delete(key=KeyPair(id, LifecycleEvents.ARCHIVE_IT)) - transact.delete(key=KeyPair(id, LifecycleEvents.NO_ACTIVITY)) - transact.delete(key=KeyPair(id, LifecycleEvents.ACCESS_PERIOD_ENDS)) - transact.delete(key=KeyPair(id, LifecycleEvents.DOES_NOT_ACCESS)) + transact.delete(key=KeyPair(id, 'SCHEDULE#REMINDER_NO_ACCESS_AFTER_3_DAYS')) + transact.delete(key=KeyPair(id, 'SCHEDULE#REMINDER_NO_ACTIVITY_AFTER_7_DAYS')) + transact.delete( + key=KeyPair(id, 'SCHEDULE#REMINDER_ACCESS_PERIOD_BEFORE_30_DAYS') + ) + transact.delete( + key=KeyPair(id, 'SCHEDULE#REMINDER_CERT_EXPIRATION_BEFORE_30_DAYS') + ) + transact.delete(key=KeyPair(id, 'SCHEDULE#SET_AS_EXPIRED')) transact.delete(key=KeyPair(id, 'parent_vacancy')) if lock_hash: - transact.delete(key=KeyPair(id, 'lock')) - transact.delete(key=KeyPair('lock', lock_hash)) + transact.delete(key=KeyPair(id, 'LOCK')) + transact.delete(key=KeyPair('LOCK', lock_hash)) if vacancy_key and course: vacancy_pk, vacancy_sk = vacancy_key.values() diff --git a/http-api/cli/jsonl2sqlite.py b/http-api/cli/jsonl2sqlite.py index 32f8924..756d269 100644 --- a/http-api/cli/jsonl2sqlite.py +++ b/http-api/cli/jsonl2sqlite.py @@ -42,7 +42,7 @@ if __name__ == '__main__': with sqlite3.connect('mydatabase.db') as conn: cursor = conn.cursor() cursor.execute( - 'CREATE TABLE IF NOT EXISTS %s (id TEXT, sk TEXT, json JSON)' + 'CREATE TABLE IF NOT EXISTS %s (pk TEXT, sk TEXT, json JSON)' % table_name ) @@ -51,10 +51,10 @@ if __name__ == '__main__': desc=f'⏳ Inserting into table {table_name}', ): cursor.execute( - 'INSERT INTO %s (id, sk, json) VALUES (:id, :sk, :json)' + 'INSERT INTO %s (pk, sk, json) VALUES (:pk, :sk, :json)' % table_name, { - 'id': record['id'], + 'pk': record['id'], 'sk': record['sk'], 'json': record, }, diff --git a/http-api/template.yaml b/http-api/template.yaml index 1e4811a..8ee9286 100644 --- a/http-api/template.yaml +++ b/http-api/template.yaml @@ -26,7 +26,7 @@ Globals: Architectures: - x86_64 Layers: - - !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:86 + - !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:96 Environment: Variables: TZ: America/Sao_Paulo diff --git a/http-api/uv.lock b/http-api/uv.lock index 36fa30d..1c5b562 100644 --- a/http-api/uv.lock +++ b/http-api/uv.lock @@ -506,7 +506,7 @@ wheels = [ [[package]] name = "layercake" -version = "0.9.12" +version = "0.9.14" source = { directory = "../layercake" } dependencies = [ { name = "arnparse" }, @@ -518,6 +518,7 @@ dependencies = [ { name = "meilisearch" }, { name = "orjson" }, { name = "passlib" }, + { name = "psycopg", extra = ["binary"] }, { name = "pycpfcnpj" }, { name = "pydantic", extra = ["email"] }, { name = "pydantic-extra-types" }, @@ -540,6 +541,7 @@ requires-dist = [ { name = "meilisearch", specifier = ">=0.34.0" }, { name = "orjson", specifier = ">=3.10.15" }, { name = "passlib", specifier = ">=1.7.4" }, + { name = "psycopg", extras = ["binary"], specifier = ">=3.2.9" }, { name = "pycpfcnpj", specifier = ">=1.8" }, { name = "pydantic", extras = ["email"], specifier = ">=2.10.6" }, { name = "pydantic-extra-types", specifier = ">=2.10.3" }, @@ -696,6 +698,41 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce", size = 49567, upload-time = "2018-02-15T19:01:27.172Z" }, ] +[[package]] +name = "psycopg" +version = "3.2.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/27/4a/93a6ab570a8d1a4ad171a1f4256e205ce48d828781312c0bbaff36380ecb/psycopg-3.2.9.tar.gz", hash = "sha256:2fbb46fcd17bc81f993f28c47f1ebea38d66ae97cc2dbc3cad73b37cefbff700", size = 158122, upload-time = "2025-05-13T16:11:15.533Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/b0/a73c195a56eb6b92e937a5ca58521a5c3346fb233345adc80fd3e2f542e2/psycopg-3.2.9-py3-none-any.whl", hash = "sha256:01a8dadccdaac2123c916208c96e06631641c0566b22005493f09663c7a8d3b6", size = 202705, upload-time = "2025-05-13T16:06:26.584Z" }, +] + +[package.optional-dependencies] +binary = [ + { name = "psycopg-binary", marker = "implementation_name != 'pypy'" }, +] + +[[package]] +name = "psycopg-binary" +version = "3.2.9" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/0b/f61ff4e9f23396aca674ed4d5c9a5b7323738021d5d72d36d8b865b3deaf/psycopg_binary-3.2.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:98bbe35b5ad24a782c7bf267596638d78aa0e87abc7837bdac5b2a2ab954179e", size = 4017127, upload-time = "2025-05-13T16:08:21.391Z" }, + { url = "https://files.pythonhosted.org/packages/bc/00/7e181fb1179fbfc24493738b61efd0453d4b70a0c4b12728e2b82db355fd/psycopg_binary-3.2.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:72691a1615ebb42da8b636c5ca9f2b71f266be9e172f66209a361c175b7842c5", size = 4080322, upload-time = "2025-05-13T16:08:24.049Z" }, + { url = "https://files.pythonhosted.org/packages/58/fd/94fc267c1d1392c4211e54ccb943be96ea4032e761573cf1047951887494/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ab464bfba8c401f5536d5aa95f0ca1dd8257b5202eede04019b4415f491351", size = 4655097, upload-time = "2025-05-13T16:08:27.376Z" }, + { url = "https://files.pythonhosted.org/packages/41/17/31b3acf43de0b2ba83eac5878ff0dea5a608ca2a5c5dd48067999503a9de/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e8aeefebe752f46e3c4b769e53f1d4ad71208fe1150975ef7662c22cca80fab", size = 4482114, upload-time = "2025-05-13T16:08:30.781Z" }, + { url = "https://files.pythonhosted.org/packages/85/78/b4d75e5fd5a85e17f2beb977abbba3389d11a4536b116205846b0e1cf744/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7e4e4dd177a8665c9ce86bc9caae2ab3aa9360b7ce7ec01827ea1baea9ff748", size = 4737693, upload-time = "2025-05-13T16:08:34.625Z" }, + { url = "https://files.pythonhosted.org/packages/3b/95/7325a8550e3388b00b5e54f4ced5e7346b531eb4573bf054c3dbbfdc14fe/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fc2915949e5c1ea27a851f7a472a7da7d0a40d679f0a31e42f1022f3c562e87", size = 4437423, upload-time = "2025-05-13T16:08:37.444Z" }, + { url = "https://files.pythonhosted.org/packages/1a/db/cef77d08e59910d483df4ee6da8af51c03bb597f500f1fe818f0f3b925d3/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a1fa38a4687b14f517f049477178093c39c2a10fdcced21116f47c017516498f", size = 3758667, upload-time = "2025-05-13T16:08:40.116Z" }, + { url = "https://files.pythonhosted.org/packages/95/3e/252fcbffb47189aa84d723b54682e1bb6d05c8875fa50ce1ada914ae6e28/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5be8292d07a3ab828dc95b5ee6b69ca0a5b2e579a577b39671f4f5b47116dfd2", size = 3320576, upload-time = "2025-05-13T16:08:43.243Z" }, + { url = "https://files.pythonhosted.org/packages/1c/cd/9b5583936515d085a1bec32b45289ceb53b80d9ce1cea0fef4c782dc41a7/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:778588ca9897b6c6bab39b0d3034efff4c5438f5e3bd52fda3914175498202f9", size = 3411439, upload-time = "2025-05-13T16:08:47.321Z" }, + { url = "https://files.pythonhosted.org/packages/45/6b/6f1164ea1634c87956cdb6db759e0b8c5827f989ee3cdff0f5c70e8331f2/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f0d5b3af045a187aedbd7ed5fc513bd933a97aaff78e61c3745b330792c4345b", size = 3477477, upload-time = "2025-05-13T16:08:51.166Z" }, + { url = "https://files.pythonhosted.org/packages/7b/1d/bf54cfec79377929da600c16114f0da77a5f1670f45e0c3af9fcd36879bc/psycopg_binary-3.2.9-cp313-cp313-win_amd64.whl", hash = "sha256:2290bc146a1b6a9730350f695e8b670e1d1feb8446597bed0bbe7c3c30e0abcb", size = 2928009, upload-time = "2025-05-13T16:08:53.67Z" }, +] + [[package]] name = "psycopg2-binary" version = "2.9.10" @@ -1035,6 +1072,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/86/39b65d676ec5732de17b7e3c476e45bb80ec64eb50737a8dce1a4178aba1/typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5", size = 45683, upload-time = "2025-03-26T03:49:40.35Z" }, ] +[[package]] +name = "tzdata" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, +] + [[package]] name = "unidecode" version = "1.4.0" diff --git a/konviva-events/app/app.py b/konviva-events/app/app.py index 04a4ce1..fce2804 100644 --- a/konviva-events/app/app.py +++ b/konviva-events/app/app.py @@ -31,6 +31,8 @@ def postback(): progress = round(Decimal(json_body['ANDAMENTO']), 2) enrollment_id = dyn.collection.get_item( KeyPair( + # Post-migration: uncomment the following line + # pk='KONVIVA', pk='konviva', sk=SortKey(json_body['ID_MATRICULA'], path_spec='enrollment_id'), ), @@ -41,7 +43,7 @@ def postback(): update_progress(enrollment_id, progress, dynamodb_persistence_layer=dyn) if status == 'COMPLETED': - set_score(enrollment_id, score, dynamodb_persistence_layer=dyn) + set_score(enrollment_id, score, progress, dynamodb_persistence_layer=dyn) return Response(status_code=HTTPStatus.NO_CONTENT) diff --git a/konviva-events/app/enrollment.py b/konviva-events/app/enrollment.py index b701a49..0bd6856 100644 --- a/konviva-events/app/enrollment.py +++ b/konviva-events/app/enrollment.py @@ -86,21 +86,27 @@ def update_progress( } ) # Remove reminders and policies that no longer apply + transact.delete( + key=KeyPair( + pk=id, + sk='CANCEL_POLICY', + ), + ) transact.delete( key=KeyPair( pk=id, sk='SCHEDULE#REMINDER_NO_ACCESS_AFTER_3_DAYS', ) ) - transact.delete( - key=KeyPair(pk=id, sk='CANCEL_POLICY'), - ) + return True def set_score( id: str, + /, score: Decimal, + progress: Decimal, *, dynamodb_persistence_layer: DynamoDBPersistenceLayer, ): @@ -138,6 +144,7 @@ def set_score( return _set_status_as_completed( id, score, + progress=progress, user_id=user_id, course_id=course_id, cert_exp_interval=int( @@ -161,6 +168,7 @@ def set_score( return _set_status_as_failed( id, score, + progress=progress, user_id=user_id, course_id=course_id, dynamodb_persistence_layer=dynamodb_persistence_layer, @@ -174,6 +182,7 @@ def _set_status_as_completed( id: str, /, score: Decimal, + progress: Decimal, *, user_id: str, course_id: str, @@ -199,8 +208,8 @@ def _set_status_as_completed( with dynamodb_persistence_layer.transact_writer() as transact: transact.update( key=KeyPair(pk=id, sk='0'), - update_expr='SET #status = :completed, score = :score, \ - updated_at = :updated_at', + update_expr='SET #status = :completed, progress = :progress, \ + score = :score, updated_at = :updated_at', cond_expr='#status = :in_progress', expr_attr_names={'#status': 'status'}, expr_attr_values={ @@ -208,6 +217,7 @@ def _set_status_as_completed( ':in_progress': 'IN_PROGRESS', ':score': score, ':updated_at': now_, + ':progress': progress, }, exc_cls=EnrollmentConflictError, ) @@ -242,6 +252,7 @@ def _set_status_as_completed( 'id': id, 'sk': 'LOCK', 'ttl': deduplication_lock_ttl, + 'hash': lock_hash, 'created_at': now_, } ) @@ -256,6 +267,12 @@ def _set_status_as_completed( ) # Remove reminders and policies that no longer apply + transact.delete( + key=KeyPair( + pk=id, + sk='CANCEL_POLICY', + ) + ) transact.delete( key=KeyPair( pk=id, @@ -282,6 +299,7 @@ def _set_status_as_failed( id: str, /, score: Decimal, + progress: Decimal, user_id: str, course_id: str, *, @@ -293,14 +311,15 @@ def _set_status_as_failed( with dynamodb_persistence_layer.transact_writer() as transact: transact.update( key=KeyPair(pk=id, sk='0'), - update_expr='SET #status = :failed, score = :score, \ - updated_at = :updated_at', + update_expr='SET #status = :failed, progress = :progress, \ + score = :score, updated_at = :updated_at', cond_expr='#status = :in_progress', expr_attr_names={'#status': 'status'}, expr_attr_values={ ':failed': 'FAILED', ':in_progress': 'IN_PROGRESS', ':score': score, + ':progress': progress, ':updated_at': now_, }, exc_cls=EnrollmentConflictError, @@ -314,6 +333,12 @@ def _set_status_as_failed( cond_expr='attribute_not_exists(sk)', ) # Remove reminders and events that no longer apply + transact.delete( + key=KeyPair( + pk=id, + sk='CANCEL_POLICY', + ) + ) transact.delete( key=KeyPair( pk=id, @@ -413,6 +438,12 @@ def _set_status_as_expired( cond_expr='attribute_not_exists(sk)', ) # Remove events and policies that no longer apply + transact.delete( + key=KeyPair( + pk=id, + sk='CANCEL_POLICY', + ), + ) transact.delete( key=KeyPair( pk=id, diff --git a/order-events/app/events/billing/append_enrollment.py b/order-events/app/events/billing/append_enrollment.py index 81fa628..b57fcea 100644 --- a/order-events/app/events/billing/append_enrollment.py +++ b/order-events/app/events/billing/append_enrollment.py @@ -59,7 +59,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool: if old_course: enrollment['course'] = old_course - created_at: datetime = fromisoformat(enrollment['create_date']) # type: ignore + created_at: datetime = fromisoformat(enrollment['created_at']) # type: ignore start_date, end_date = get_billing_period( billing_day=new_image['billing_day'], date_=created_at, @@ -122,9 +122,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool: 'user': pick(('id', 'name'), enrollment['user']), 'course': pick(('id', 'name'), enrollment['course']), 'unit_price': course['unit_price'], - # Post-migration: uncomment the following line - # 'enrolled_at': enrollment['created_at'], - 'enrolled_at': enrollment['create_date'], + 'enrolled_at': enrollment['created_at'], 'created_at': now_, } # Add author if present diff --git a/order-events/app/events/billing/cancel_enrollment.py b/order-events/app/events/billing/cancel_enrollment.py index 0229cbd..b82761f 100644 --- a/order-events/app/events/billing/cancel_enrollment.py +++ b/order-events/app/events/billing/cancel_enrollment.py @@ -33,7 +33,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool: + SortKey('canceled', path_spec='author', rename_key='canceled_by') ) - created_at: datetime = fromisoformat(new_image['create_date']) # type: ignore + created_at: datetime = fromisoformat(new_image['created_at']) # type: ignore start_date, end_date = get_billing_period( billing_day=int(subscription['billing_day']), date_=created_at,