From e3318b7e1e6692d906a1ab9ee26267b0218bfde9 Mon Sep 17 00:00:00 2001 From: Mojtaba-z Date: Tue, 30 Dec 2025 16:57:15 +0330 Subject: [PATCH] import - BankAccountDeviceLink for set device to a bank_account/changes in stakeholders & pos --- apps/authentication/services/service.py | 10 ++++-- .../migrations/0080_bankaccountdevicelink.py | 36 +++++++++++++++++++ apps/pos_device/models.py | 30 +++++++++++++++- apps/pos_device/services/services.py | 8 ++--- .../web/api/v1/serilaizers/device.py | 10 ++++++ apps/pos_device/web/api/v1/urls.py | 8 ++++- apps/pos_device/web/api/v1/viewsets/device.py | 7 ++++ .../api/v1/serializers/quota_serializers.py | 6 +++- 8 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 apps/pos_device/migrations/0080_bankaccountdevicelink.py diff --git a/apps/authentication/services/service.py b/apps/authentication/services/service.py index 6639db2..c7c1509 100644 --- a/apps/authentication/services/service.py +++ b/apps/authentication/services/service.py @@ -42,8 +42,14 @@ def get_all_org_type_child(org_type: OrganizationType = None) -> typing.Any: return descendants -def get_bank_info(org): - bank = org.bank_information.first() +def get_bank_info(org, device=None): + if device: + # get organization bank account that set to a device + bank_account_device_links = org.bank_account_device_links.filter(device=device) + bank = bank_account_device_links.first().bank_account \ + if bank_account_device_links.exists() else org.bank_information.first() + else: + bank = org.bank_information.first() if not bank: return {} return { diff --git a/apps/pos_device/migrations/0080_bankaccountdevicelink.py b/apps/pos_device/migrations/0080_bankaccountdevicelink.py new file mode 100644 index 0000000..6decbbc --- /dev/null +++ b/apps/pos_device/migrations/0080_bankaccountdevicelink.py @@ -0,0 +1,36 @@ +# Generated by Django 5.0 on 2025-12-30 12:30 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('authentication', '0057_organization_purchase_policy'), + ('pos_device', '0079_stakeholdershareamount_org_quota_stat'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='BankAccountDeviceLink', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('create_date', models.DateTimeField(auto_now_add=True)), + ('modify_date', models.DateTimeField(auto_now=True)), + ('creator_info', models.CharField(max_length=100, null=True)), + ('modifier_info', models.CharField(max_length=100, null=True)), + ('trash', models.BooleanField(default=False)), + ('bank_account', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='linked_devices', to='authentication.bankaccountinformation')), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL)), + ('device', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='linked_bank_accounts', to='pos_device.device')), + ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)), + ('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='bank_account_device_links', to='authentication.organization')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/pos_device/models.py b/apps/pos_device/models.py index 0d62f7e..771787b 100644 --- a/apps/pos_device/models.py +++ b/apps/pos_device/models.py @@ -4,7 +4,7 @@ import string from django.contrib.postgres.fields import ArrayField from django.db import models -from apps.authentication.models import Organization, City, Province +from apps.authentication.models import Organization, City, Province, BankAccountInformation from apps.authorization.models import UserRelations from apps.core.models import BaseModel from apps.product.models import Product, Broker, QuotaBrokerValue, QuotaDistribution, OrganizationQuotaStats @@ -70,6 +70,34 @@ class Device(BaseModel): return super(Device, self).save(*args, **kwargs) +class BankAccountDeviceLink(BaseModel): + organization = models.ForeignKey( + Organization, + on_delete=models.CASCADE, + related_name='bank_account_device_links', + null=True + ) + bank_account = models.ForeignKey( + BankAccountInformation, + on_delete=models.CASCADE, + related_name='linked_devices', + null=True + ) + device = models.ForeignKey( + Device, + on_delete=models.CASCADE, + related_name='linked_bank_accounts', + null=True + ) + + def __str__(self): + return (f'{self.id} --> organization: {self.organization.name}/' + f'{self.organization.id} --> device: {self.device.serial}') + + def save(self, *args, **kwargs): + return super(BankAccountDeviceLink, self).save(*args, **kwargs) + + class DeviceActivationCode(BaseModel): device = models.ForeignKey( Device, diff --git a/apps/pos_device/services/services.py b/apps/pos_device/services/services.py index b975d93..6c8107a 100644 --- a/apps/pos_device/services/services.py +++ b/apps/pos_device/services/services.py @@ -59,7 +59,7 @@ def pos_organizations_sharing_information( sharing_information_list.append({ "organization_name": item.organization.name, - "bank_account": get_bank_info(item.organization), + "bank_account": get_bank_info(item.organization, device=device), "broker": item.broker.name, "amount": broker_value_map.get(item.broker_id), "agency": False, @@ -115,11 +115,7 @@ def agency_organization_pos_info( pos_sharing_list.append({ "organization_name": agency.name, - "bank_account": { - "credit_card": agency.bank_information.first().card, - "sheba": "IR" + agency.bank_information.first().sheba, - "account": agency.bank_information.first().account, - } if agency.bank_information.exists() else {}, + "bank_account": get_bank_info(org=agency, device=device), "amount": agc_share_amount, "agency": True, "default_account": True diff --git a/apps/pos_device/web/api/v1/serilaizers/device.py b/apps/pos_device/web/api/v1/serilaizers/device.py index aa87cf9..929384d 100644 --- a/apps/pos_device/web/api/v1/serilaizers/device.py +++ b/apps/pos_device/web/api/v1/serilaizers/device.py @@ -81,8 +81,12 @@ class StakeHoldersSerializer(ModelSerializer): def to_representation(self, instance): representation = super().to_representation(instance) + # get organization bank account that set to a device + bank_account_device_links = instance.organization.bank_account_device_links.filter(device=instance.device) + representation['bank_account'] = BankAccountSerializer( instance.organization.bank_information.all().first() + if not bank_account_device_links.exists() else bank_account_device_links.first().bank_account ).data representation['device'] = instance.device.device_identity @@ -127,3 +131,9 @@ class StakeHolderShareAmountSerializer(ModelSerializer): representation['stakeholders'] = StakeHoldersSerializer(instance.stakeholders).data return representation + + +class BankAccountDeviceLinkSerializer(ModelSerializer): + class Meta: + model = pos_models.BankAccountDeviceLink + fields = '__all__' diff --git a/apps/pos_device/web/api/v1/urls.py b/apps/pos_device/web/api/v1/urls.py index 529ad7d..406c5ef 100644 --- a/apps/pos_device/web/api/v1/urls.py +++ b/apps/pos_device/web/api/v1/urls.py @@ -1,5 +1,6 @@ from django.urls import path, include from rest_framework.routers import DefaultRouter + from .viewsets import client as client_views from .viewsets import device as device_views @@ -11,7 +12,12 @@ router.register(r'device', device_views.DeviceViewSet, basename='device') router.register(r'device_assignment', device_views.DeviceAssignmentViewSet, basename='device_assignment') router.register(r'stake_holders', device_views.StakeHoldersViewSet, basename='stake_holders') router.register(r'holders_share', device_views.StakeHolderShareAmountViewSet, basename='holders_share') +router.register( + r'bank_account_device_link', + device_views.BankAccountDeviceLinkViewSet, + basename='bank_account_device_link' +) urlpatterns = [ path('v1/pos/', include(router.urls)) -] \ No newline at end of file +] diff --git a/apps/pos_device/web/api/v1/viewsets/device.py b/apps/pos_device/web/api/v1/viewsets/device.py index 3dd49fd..61e489e 100644 --- a/apps/pos_device/web/api/v1/viewsets/device.py +++ b/apps/pos_device/web/api/v1/viewsets/device.py @@ -24,7 +24,9 @@ from apps.core.mixins.search_mixin import DynamicSearchMixin from apps.core.mixins.soft_delete_mixin import SoftDeleteMixin from apps.core.services.visibility_service import apply_visibility_filter_by_org_type from apps.pos_device import models as pos_models +from apps.pos_device.models import BankAccountDeviceLink from apps.pos_device.web.api.v1.serilaizers import device as device_serializer +from apps.pos_device.web.api.v1.serilaizers.device import BankAccountDeviceLinkSerializer from apps.pos_device.web.api.v1.viewsets.client import POSClientViewSet from apps.product.models import Broker, OrganizationQuotaStats from apps.product.web.api.v1.viewsets.quota_distribution_api import QuotaDistributionViewSet @@ -515,3 +517,8 @@ class StakeHolderShareAmountViewSet(viewsets.ModelViewSet, DynamicSearchMixin, S if page is not None: # noqa serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) + + +class BankAccountDeviceLinkViewSet(BaseViewSet, viewsets.ModelViewSet, DynamicSearchMixin, SoftDeleteMixin): + queryset = BankAccountDeviceLink.objects.select_related('organization', 'bank_account', 'device') + serializer_class = BankAccountDeviceLinkSerializer diff --git a/apps/product/pos/api/v1/serializers/quota_serializers.py b/apps/product/pos/api/v1/serializers/quota_serializers.py index 2a9ae08..8229649 100644 --- a/apps/product/pos/api/v1/serializers/quota_serializers.py +++ b/apps/product/pos/api/v1/serializers/quota_serializers.py @@ -314,8 +314,12 @@ class OrganizationQuotaStatsSerializer(serializers.ModelSerializer): owner_org=organization ) + # get organization bank account that set to a device + bank_account_device_links = organization.bank_account_device_links.filter(device=device) + representation['pricing'] = { # noqa - 'main_account_sheba': "IR" + organization.bank_information.first().sheba, + 'main_account_sheba': "IR" + organization.bank_information.first().sheba + if not bank_account_device_links.exists() else bank_account_device_links.first().bank_account.sheba, 'pricing_attributes': quota_attribute_value(instance.quota), 'sharing': sharing_list, 'base_prices': quota_pricing_items_by_type(instance.quota, sharing=sharing_list)