From 633a401aa69c369d6ddf917257e3870d97f1a841 Mon Sep 17 00:00:00 2001 From: anhduy-tech Date: Wed, 31 Dec 2025 16:44:06 +0700 Subject: [PATCH] changes --- api/__pycache__/settings.cpython-313.pyc | Bin 3451 -> 3451 bytes app/__pycache__/consumers.cpython-313.pyc | Bin 5394 -> 5620 bytes app/__pycache__/models.cpython-313.pyc | Bin 133888 -> 133973 bytes app/consumers.py | 77 +++++++++++------- ...ple_taxcode_remove_people_zalo_and_more.py | 32 ++++++++ app/models.py | 5 +- 6 files changed, 81 insertions(+), 33 deletions(-) create mode 100644 app/migrations/0341_rename_facebook_people_taxcode_remove_people_zalo_and_more.py diff --git a/api/__pycache__/settings.cpython-313.pyc b/api/__pycache__/settings.cpython-313.pyc index 19c59008910e27baeffc8e675d9cf7a0f1dcbb0c..6c33a0b030cfcb1f9c0fbf08849d4fc621b8d592 100644 GIT binary patch delta 19 Zcmew@^;?STGcPX}0}up#*vM7L3jjdV1;+pY delta 19 Zcmew@^;?STGcPX}0}#aaZsaQD1pq%L1#bWV diff --git a/app/__pycache__/consumers.cpython-313.pyc b/app/__pycache__/consumers.cpython-313.pyc index 62d1510e586a85ff4df6416b629f5148ded979ef..3d2d9ac273c7fbf68cda808c8a15741704917017 100644 GIT binary patch delta 1559 zcmb7DTW=dh6rNdMc5Sb{H^-MajuShi*(P?JN^YR3LT<$=QYztPr9vu2uDwm%B(}S8 zC{;k_@?svK3iFVJm%1;g2qBffQ1v45WXqLoEP?U>m3TmkNgx7Vn047iMMZ*<=G${- z&iT%nbIyKx@?g-hZ?{_kUVij?=92KW;}4sa!s>^3?|*}MI^37AX!p-9$sB`EPqHT2 zD3NHA_Qt5AGqY?S_}d{#0w9}oEsebv0rs$%@O_q)N%TJLhZMR+559KnZ;>tk@Gwc% zGZz6!hAv9aUXiHBsN#VqZAdvVdknJ<@G|bZT9ZzlWjWyxY<>PO{ zi#F{pmu!}pm!M3Cac%UliB1%D!~}=zI12C%rm-aa=o6~*RF;ug?V&OwIZj~0<4E(} zI=@a81Q&0{2$ZK>#VjkcM|=~QkA3drIoZcXY?4`t)A}aCHsUI0xqs~jr*%MQ zIu9hUgf=L9bP@L-x8#5IuzL|uAPz6YC~)B`ybE}b;O!VST!Es1Hkg5;Ewz-)E-DL! z8+oN^R+UsvO*e^5T8-d@n`By9Q3^`B$*4+lrI1-w7S{9WWI+)PZ37io1E`Z2L_!_W zWUZ`C^Oej>K~Y86B=XDZE83A!&0Syn0*GvrO<%>#0zqG7hI zxN*p%1+IfswJB6*(7Rm!1cztuC-5Hlk?Q}B5_fs`wx!AkOJu|4-&)>WE{AKbXvx-a z`nG4Q&KK%VvFa4>^wpfhCCd}W^SKSxxUq>nGoanuV$Bw6@UE@MW~AW}ce{ofp`k_~ zRDPq-2P$o<1-T0qZ^>0`wsuymZO_8wF_ZZYWw0#j2rL zEi_u8dm52bch6VC<5(wpfjo#)rSJUBYeqV5&DBg7-o&?lIo$ zo3dc#ei+7m#`}>oSbi`7<9_1HA&@*^I`dyS6-vnKnXhW<7N+Sm(; zcnEzgEWjD`v*3fX$l~mRA=K{-nqP$`MopnP=k?B6ZE#W=Xd~35&aURt>nqB*x`e)W R2KfyLnTinp6=*`+?l0SvNoW87 delta 1270 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 diff --git a/app/consumers.py b/app/consumers.py index b9dfdb87..82cfba8a 100644 --- a/app/consumers.py +++ b/app/consumers.py @@ -59,8 +59,10 @@ class DataConsumer(AsyncJsonWebsocketConsumer): from .views import get_serializer payload = event["payload"] + change_type = payload.get("change_type") record_id = payload["record"].get("id") - if not record_id: + + if not record_id or not change_type: return model_name_lower = payload["name"] @@ -71,7 +73,19 @@ class DataConsumer(AsyncJsonWebsocketConsumer): if not client_params: return # This client is not subscribed to this model. - # 2. Check if the updated record ID could possibly match the client's filter + # 2. Handle DELETION event + if change_type == 'deleted': + await self.send_json({ + "type": "realtime_update", + "payload": { + "name": model_name_lower, + "change_type": "deleted", + "record": {"id": record_id} + } + }) + return + + # 3. Handle CREATION and UPDATE events Model, _ = get_serializer(model_name_lower) if not Model: return @@ -80,36 +94,37 @@ class DataConsumer(AsyncJsonWebsocketConsumer): filter_q = Q() filter_dict = client_params.get('filter') if isinstance(filter_dict, dict): - for key, value in filter_dict.items(): - filter_q.add(Q(**{key: value}), Q.AND) + filter_q = Q(**filter_dict) - # Combine the client's filter with the specific record's ID - combined_filter = Q(pk=record_id) & filter_q - # Check if the object actually exists with the combined filter - record_exists = await database_sync_to_async(Model.objects.filter(combined_filter).exists)() - if not record_exists: - return # The record does not match the client's filter, so don't send. + record_exists = await database_sync_to_async( + Model.objects.filter(Q(pk=record_id) & filter_q).exists + )() - # 3. Re-run the original query for just this single object. - # This correctly applies 'values', 'distinct_values', 'summary', etc. - single_record_params = client_params.copy() - single_record_params['filter'] = {'pk': record_id} - - data = await database_sync_to_async(execute_data_query)(model_name_capitalized, single_record_params) + if record_exists: + # If record matches filter, re-run the query to get the correctly shaped data + single_record_params = client_params.copy() + single_record_params['filter'] = {'pk': record_id} + + data = await database_sync_to_async(execute_data_query)(model_name_capitalized, single_record_params) - # If the query returns no data (e.g., record was deleted or no longer matches), do nothing. - if not data or not data.get('rows'): - return - - # 4. Build the final payload with the correctly-shaped record - payload_for_client = { - "name": model_name_lower, - "record": data['rows'][0] - } - - # 5. Send the final, tailored payload to the client - await self.send_json({ - "type": "realtime_update", - "payload": payload_for_client - }) + if data and data.get('rows'): + payload_for_client = { + "name": model_name_lower, + "change_type": change_type, + "record": data['rows'][0] + } + await self.send_json({ + "type": "realtime_update", + "payload": payload_for_client + }) + else: + # If the record exists but no longer matches the filter, treat as a deletion for this client + await self.send_json({ + "type": "realtime_update", + "payload": { + "name": model_name_lower, + "change_type": "deleted", + "record": {"id": record_id} + } + }) diff --git a/app/migrations/0341_rename_facebook_people_taxcode_remove_people_zalo_and_more.py b/app/migrations/0341_rename_facebook_people_taxcode_remove_people_zalo_and_more.py new file mode 100644 index 00000000..f80bbcc1 --- /dev/null +++ b/app/migrations/0341_rename_facebook_people_taxcode_remove_people_zalo_and_more.py @@ -0,0 +1,32 @@ +# Generated by Django 5.1.7 on 2025-12-31 09:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0340_file_hashtag_co_ownership'), + ] + + operations = [ + migrations.RenameField( + model_name='people', + old_name='facebook', + new_name='taxcode', + ), + migrations.RemoveField( + model_name='people', + name='zalo', + ), + migrations.AddField( + model_name='transaction_detail', + name='amount_received', + field=models.DecimalField(decimal_places=2, max_digits=15, null=True), + ), + migrations.AddField( + model_name='transaction_detail', + name='amount_remaining', + field=models.DecimalField(decimal_places=2, max_digits=15, null=True), + ), + ] diff --git a/app/models.py b/app/models.py index 6831d794..28a0c21f 100644 --- a/app/models.py +++ b/app/models.py @@ -1200,8 +1200,7 @@ class People(models.Model): issued_date = models.DateField(null=True) address = models.CharField(max_length=200, null=True) contact_address = models.CharField(max_length=200, null=True) - zalo = models.CharField(max_length=20, null=True) - facebook = models.CharField(max_length=100, null=True) + taxcode = models.CharField(max_length=100, null=True) note = models.TextField(null=True) country = models.ForeignKey(Country, null=True, related_name='+', on_delete=models.PROTECT, default=1) company = models.ForeignKey(Company, null=True, related_name='+', on_delete=models.PROTECT) @@ -1371,6 +1370,8 @@ class Transaction_Detail(AutoCodeModel): code = models.CharField(max_length=30, null=True, unique=True) date = models.DateField() amount = models.DecimalField(max_digits=15, decimal_places=2, null=True) + amount_remaining = models.DecimalField(max_digits=15, decimal_places=2, null=True) + amount_received = models.DecimalField(max_digits=15, decimal_places=2, null=True) due_date = models.DateField(null=True) transaction = models.ForeignKey(Transaction, null=False, related_name='resvtxn', on_delete=models.PROTECT) phase = models.ForeignKey(Transaction_Phase, null=False, related_name='+', on_delete=models.PROTECT)