initial commit

This commit is contained in:
2025-08-05 10:26:39 +05:00
commit b7412bbef6
298 changed files with 10533 additions and 0 deletions

View File

View File

@@ -0,0 +1 @@
from .banks import * # noqa

View File

@@ -0,0 +1,23 @@
from django.contrib import admin
from unfold.admin import ModelAdmin # type: ignore
from core.apps.banks.models import BankModel
@admin.register(BankModel)
class BankAdmin(ModelAdmin):
list_display = (
"name",
"bic_code",
"created_at",
"updated_at",
)
search_fields = (
"name",
"bic_code",
"created_at",
"updated_at"
)
list_display_links = (
"name",
)

6
core/apps/banks/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ModuleConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "core.apps.banks"

View File

@@ -0,0 +1 @@
from .banks import * # noqa

View File

@@ -0,0 +1,13 @@
from django_filters import rest_framework as filters
from core.apps.banks.models import BanksModel
class BanksFilter(filters.FilterSet):
# name = filters.CharFilter(field_name="name", lookup_expr="icontains")
class Meta:
model = BanksModel
fields = [
"name",
]

View File

@@ -0,0 +1 @@
from .banks import * # noqa

View File

@@ -0,0 +1,10 @@
from django import forms
from core.apps.banks.models import BanksModel
class BanksForm(forms.ModelForm):
class Meta:
model = BanksModel
fields = "__all__"

View File

@@ -0,0 +1,65 @@
# Generated by Django 5.2.4 on 2025-08-01 09:53
import django.core.validators
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="BankModel",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name="ID"
),
),
("created_at", models.DateTimeField(auto_now_add=True, verbose_name="Created At")),
("updated_at", models.DateTimeField(auto_now=True, verbose_name="Updated At")),
(
"name",
models.CharField(
max_length=255,
unique=True,
validators=[
django.core.validators.RegexValidator(
message="Enter a valid bank name. Only letters, numbers, spaces, hyphens, ampersands, commas, periods, apostrophes, and parentheses are allowed. Length must be 2100 characters.",
regex="^[A-Za-z0-9À-ÿ&'\\-.,() ]{2,100}$",
)
],
verbose_name="Name",
),
),
(
"bic_code",
models.CharField(
unique=True,
validators=[
django.core.validators.RegexValidator(
code="Invalid BIC/SWIFT code",
message="Enter a valid BIC/SWIFT code (8 or 11 uppercase letters/numbers).",
regex="^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$",
)
],
verbose_name="BIC code",
),
),
],
options={
"verbose_name": "Bank",
"verbose_name_plural": "Banks",
"db_table": "banks",
"indexes": [
models.Index(fields=["bic_code"], name="banks_bic_code_inx"),
models.Index(fields=["name"], name="banks_name_inx"),
],
},
),
]

View File

View File

@@ -0,0 +1 @@
from .banks import * # noqa

View File

@@ -0,0 +1,68 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from core.utils.base_model import UUIDPrimaryKeyBaseModel
from core.apps.banks.validators.banks import (
BankValidator, # used with BankModel.clean
bic_validator, # validates bic
name_validator, # validates bank name
)
class BankModel(UUIDPrimaryKeyBaseModel):
name = models.CharField(
_("Name"),
max_length=255,
validators=[
name_validator,
],
null=False,
blank=False,
unique=True,
)
bic_code = models.CharField(
_("BIC code"),
validators=[
bic_validator,
],
null=False,
blank=False,
unique=True,
)
def __str__(self):
return self.name
@classmethod
def _create_fake(cls):
return cls.objects.create(
name="Mock TBC Bank",
bic_code="MOCKUZ22"
)
def clean(self) -> None:
super().clean()
validator = BankValidator(self)
validator()
class Meta: # type: ignore
db_table = "banks"
verbose_name = _("Bank")
verbose_name_plural = _("Banks")
indexes = [
models.Index(
fields=["bic_code"],
name="banks_bic_code_inx"
),
models.Index(
fields=["name"],
name="banks_name_inx"
),
]

View File

@@ -0,0 +1 @@
from .banks import * # noqa

View File

@@ -0,0 +1,12 @@
from rest_framework import permissions
class BanksPermission(permissions.BasePermission):
def __init__(self) -> None: ...
def __call__(self, *args, **kwargs):
return self
def has_permission(self, request, view):
return True

View File

@@ -0,0 +1 @@
from .banks import * # noqa

View File

@@ -0,0 +1 @@
from .banks import * # noqa

View File

@@ -0,0 +1,35 @@
from rest_framework import serializers # type: ignore
from core.apps.banks.models import BankModel
class BaseBankSerializer(serializers.ModelSerializer):
class Meta:
model = BankModel
fields = "__all__"
read_only_fields = (
"id",
"created_at",
"updated_at"
)
class ListBankSerializer(BaseBankSerializer):
class Meta(BaseBankSerializer.Meta): ...
class RetrieveBankSerializer(BaseBankSerializer):
class Meta(BaseBankSerializer.Meta): ...
class CreateBankSerializer(BaseBankSerializer):
class Meta(BaseBankSerializer.Meta): ...
class UpdateBankSerializer(BaseBankSerializer):
class Meta(BaseBankSerializer.Meta): ...
class DestroyBankSerializer(BaseBankSerializer):
class Meta(BaseBankSerializer.Meta): ...

View File

@@ -0,0 +1 @@
from .banks import * # noqa

View File

@@ -0,0 +1,8 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from core.apps.banks.models import BanksModel
@receiver(post_save, sender=BanksModel)
def BanksSignal(sender, instance, created, **kwargs): ...

View File

@@ -0,0 +1 @@
from .test_banks import * # noqa

View File

@@ -0,0 +1,47 @@
from django.test import TestCase
from django.urls import reverse
from rest_framework.test import APIClient
from core.apps.banks.models import BanksModel
class BanksTest(TestCase):
def _create_data(self):
return BanksModel._create_fake()
def setUp(self):
self.client = APIClient()
self.instance = self._create_data()
self.urls = {
"list": reverse("Banks-list"),
"retrieve": reverse("Banks-detail", kwargs={"pk": self.instance.pk}),
"retrieve-not-found": reverse("Banks-detail", kwargs={"pk": 1000}),
}
def test_create(self):
self.assertTrue(True)
def test_update(self):
self.assertTrue(True)
def test_partial_update(self):
self.assertTrue(True)
def test_destroy(self):
self.assertTrue(True)
def test_list(self):
response = self.client.get(self.urls["list"])
self.assertTrue(response.json()["status"])
self.assertEqual(response.status_code, 200)
def test_retrieve(self):
response = self.client.get(self.urls["retrieve"])
self.assertTrue(response.json()["status"])
self.assertEqual(response.status_code, 200)
def test_retrieve_not_found(self):
response = self.client.get(self.urls["retrieve-not-found"])
self.assertFalse(response.json()["status"])
self.assertEqual(response.status_code, 404)

View File

@@ -0,0 +1 @@
from .banks import * # noqa

View File

@@ -0,0 +1,8 @@
from modeltranslation.translator import TranslationOptions, register
from core.apps.banks.models import BanksModel
@register(BanksModel)
class BanksTranslation(TranslationOptions):
fields = []

11
core/apps/banks/urls.py Normal file
View File

@@ -0,0 +1,11 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views
router = DefaultRouter()
router.register(r"banks", views.BankView, "banks")
urlpatterns = [
path("", include(router.urls)),
]

View File

@@ -0,0 +1 @@
from .banks import * # noqa

View File

@@ -0,0 +1,33 @@
# from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django.utils.translation import gettext_lazy as _
from django_core.models.base import AbstractBaseModel # type: ignore
bic_validator = RegexValidator(
regex=r'^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$',
message=_(
"Enter a valid BIC/SWIFT code "
"(8 or 11 uppercase letters/numbers)."
),
code='Invalid BIC/SWIFT code'
)
name_validator = RegexValidator(
regex=r"^[A-Za-z0-9À-ÿ&'\-.,() ]{2,100}$",
message=_(
"Enter a valid bank name. "
"Only letters, numbers, spaces, hyphens, ampersands, "
"commas, periods, apostrophes, and parentheses are "
"allowed. Length must be 2100 characters."
)
)
class BankValidator:
def __init__(self, instance: AbstractBaseModel):
self.instance = instance
def __call__(self):
return

View File

@@ -0,0 +1 @@
from .banks import * # noqa

View File

@@ -0,0 +1,29 @@
from django_core.mixins import BaseViewSetMixin
from drf_spectacular.utils import extend_schema
from rest_framework.permissions import IsAdminUser
from rest_framework.viewsets import ModelViewSet
from core.apps.banks.models import BankModel
from core.apps.banks.serializers.banks import (
CreateBankSerializer,
ListBankSerializer,
RetrieveBankSerializer,
UpdateBankSerializer,
DestroyBankSerializer,
)
@extend_schema(tags=["Banks"])
class BankView(BaseViewSetMixin, ModelViewSet):
queryset = BankModel.objects.all()
serializer_class = ListBankSerializer
permission_classes = [IsAdminUser]
action_permission_classes = {}
action_serializer_class = {
"list": ListBankSerializer,
"retrieve": RetrieveBankSerializer,
"create": CreateBankSerializer,
"update": UpdateBankSerializer,
"destroy": DestroyBankSerializer,
}