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,3 @@
from .accounts import * # noqa
from .companies import * # noqa
from .folders import * # noqa

View File

@@ -0,0 +1,27 @@
from django.contrib import admin
from unfold.admin import ModelAdmin # type: ignore
from core.apps.companies.models import CompanyAccountModel
@admin.register(CompanyAccountModel)
class DirectorAdmin(ModelAdmin):
list_display = (
"user_name",
"role",
"company",
"created_at",
)
list_display_links = (
"user_name",
"role",
)
search_fields = (
"user",
"role",
"company",
"created_at",
"updated_at",
)

View File

@@ -0,0 +1,32 @@
from django.contrib import admin
from unfold.admin import ModelAdmin # type: ignore
from core.apps.companies.models import CompanyModel
@admin.register(CompanyModel)
class CompanyAdmin(ModelAdmin):
list_display = (
"name",
"company_code",
"phone",
"email",
"iik_code",
"legal_address",
"real_address",
"created_at",
)
search_fields = (
"name",
"company_code",
"phone",
"email",
"iik_code",
"legal_address",
"real_address",
"created_at",
"updated_at"
)
list_display_links = (
"name",
)

View File

@@ -0,0 +1,24 @@
from django.contrib import admin
from unfold.admin import ModelAdmin # type: ignore
from core.apps.companies.models import CompanyFolderModel
@admin.register(CompanyFolderModel)
class FolderAdmin(ModelAdmin):
list_display = (
"name",
"company",
"created_at"
)
search_fields = (
"name",
"company",
"created_at",
"updated_at",
)
list_display_links = (
"name",
)

View File

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

View File

@@ -0,0 +1,3 @@
from .accounts import * # noqa
from .companies import * # noqa
from .folders import * # noqa

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
from .accounts import * # noqa
from .companies import * # noqa
from .folders import * # noqa

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,257 @@
# Generated by Django 5.2.4 on 2025-08-01 09:53
import django.core.validators
import django.db.models.deletion
import uuid
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
("banks", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="CompanyModel",
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=512,
validators=[
django.core.validators.RegexValidator(
message="Company name contains invalid characters.",
regex="^[A-Za-z0-9\\s\\.\\-\\,\\&]+$",
),
django.core.validators.MinLengthValidator(3),
],
verbose_name="name",
),
),
(
"pinfl_code",
models.CharField(
blank=True,
null=True,
validators=[
django.core.validators.RegexValidator(
message="PINFL code must be exactly 14 digits.", regex="^\\d{14}$"
)
],
verbose_name="PINFL code",
),
),
(
"iin_code",
models.CharField(
blank=True,
null=True,
validators=[
django.core.validators.RegexValidator(
message="IIN code must be exactly 14 digits.", regex="^\\d{14}$"
)
],
verbose_name="IIN code",
),
),
(
"phone",
models.CharField(
max_length=25,
unique=True,
validators=[
django.core.validators.RegexValidator(
message="Enter a valid international phone number (E.164 format, e.g., +14155552671).",
regex="^\\+?[1-9]\\d{1,14}$",
)
],
verbose_name="Phone Number",
),
),
(
"email",
models.CharField(
max_length=255,
unique=True,
validators=[django.core.validators.EmailValidator()],
verbose_name="Email Address",
),
),
(
"iik_code",
models.CharField(
blank=True,
max_length=30,
null=True,
validators=[
django.core.validators.RegexValidator(
message="IIK code must be alphanumeric and between 10 and 30 characters.",
regex="^[A-Z0-9]{10,30}$",
)
],
verbose_name="IIK code",
),
),
(
"legal_address",
models.CharField(
blank=True,
max_length=512,
null=True,
validators=[django.core.validators.MinLengthValidator(10)],
verbose_name="Legal Address",
),
),
(
"real_address",
models.CharField(
blank=True,
max_length=512,
null=True,
validators=[django.core.validators.MinLengthValidator(10)],
verbose_name="Real Address",
),
),
("logo", models.ImageField(upload_to="", verbose_name="Logo")),
(
"signature_authority_document",
models.FileField(upload_to="", verbose_name="Signature Authority Document"),
),
(
"bank",
models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to="banks.bankmodel"
),
),
],
options={
"verbose_name": "Company",
"verbose_name_plural": "Companies",
"db_table": "companies",
},
),
migrations.CreateModel(
name="CompanyFolderModel",
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=150,
validators=[
django.core.validators.MaxLengthValidator(150),
django.core.validators.MinLengthValidator(3),
],
verbose_name="name",
),
),
(
"company",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT, related_name="folders", to="companies.companymodel"
),
),
],
options={
"verbose_name": "Company Folder",
"verbose_name_plural": "Company Folders",
"db_table": "company_folders",
},
),
migrations.CreateModel(
name="CompanyAccountModel",
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")),
("role", models.CharField(blank=True, max_length=255, null=True, verbose_name="Role")),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="companies",
to=settings.AUTH_USER_MODEL,
verbose_name="User",
),
),
(
"company",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="accounts",
to="companies.companymodel",
verbose_name="Company",
),
),
],
options={
"verbose_name": "Company Account",
"verbose_name_plural": "Company Accounts",
"db_table": "company_accounts",
},
),
migrations.AddIndex(
model_name="companymodel",
index=models.Index(fields=["phone"], name="companies_phone_inx"),
),
migrations.AddIndex(
model_name="companymodel",
index=models.Index(fields=["email"], name="companies_email_inx"),
),
migrations.AddIndex(
model_name="companymodel",
index=models.Index(fields=["iin_code"], name="companies_iin_code_inx"),
),
migrations.AddIndex(
model_name="companymodel",
index=models.Index(fields=["pinfl_code"], name="companies_pinfl_code_inx"),
),
migrations.AddIndex(
model_name="companymodel",
index=models.Index(fields=["name"], name="companies_name_inx"),
),
migrations.AddConstraint(
model_name="companyfoldermodel",
constraint=models.UniqueConstraint(
fields=("name", "company"), name="company_folders_name_company_unique_constraint"
),
),
migrations.AddIndex(
model_name="companyaccountmodel",
index=models.Index(fields=["user"], name="company_accounts_user_inx"),
),
migrations.AddIndex(
model_name="companyaccountmodel",
index=models.Index(fields=["company"], name="company_accounts_company_inx"),
),
migrations.AlterUniqueTogether(
name="companyaccountmodel",
unique_together={("user", "company")},
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 5.2.4 on 2025-08-01 10:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("companies", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="companymodel",
name="logo",
field=models.ImageField(blank=True, null=True, upload_to="", verbose_name="Logo"),
),
migrations.AlterField(
model_name="companymodel",
name="signature_authority_document",
field=models.FileField(blank=True, null=True, upload_to="", verbose_name="Signature Authority Document"),
),
]

View File

@@ -0,0 +1,21 @@
# Generated by Django 5.2.4 on 2025-08-01 12:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("companies", "0002_alter_companymodel_logo_and_more"),
("contracts", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="companyfoldermodel",
name="contracts",
field=models.ManyToManyField(
related_name="folders", to="contracts.contractmodel", verbose_name="Contracts"
),
),
]

View File

@@ -0,0 +1,25 @@
# Generated by Django 5.2.4 on 2025-08-04 09:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("companies", "0003_companyfoldermodel_contracts"),
]
operations = [
migrations.AlterField(
model_name="companymodel",
name="logo",
field=models.ImageField(blank=True, max_length=1024, null=True, upload_to="", verbose_name="Logo"),
),
migrations.AlterField(
model_name="companymodel",
name="signature_authority_document",
field=models.FileField(
blank=True, max_length=1024, null=True, upload_to="", verbose_name="Signature Authority Document"
),
),
]

View File

@@ -0,0 +1,3 @@
from .accounts import * # noqa
from .companies import * # noqa
from .folders import * # noqa

View File

@@ -0,0 +1,77 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.contrib.auth import get_user_model
from core.utils.base_model import UUIDPrimaryKeyBaseModel
from core.apps.companies.models.companies import CompanyModel
from core.apps.companies.validators.accounts import (
CompanyAccountValidator
)
UserModel = get_user_model()
class CompanyAccountModel(UUIDPrimaryKeyBaseModel):
role = models.CharField(
_("Role"),
max_length=255,
null=True,
blank=True,
)
user = models.ForeignKey( # type: ignore
UserModel,
on_delete=models.CASCADE,
verbose_name=_("User"),
null=False,
blank=False,
related_name="companies"
)
company = models.ForeignKey( # type: ignore
CompanyModel,
on_delete=models.CASCADE,
verbose_name=_("Company"),
null=False,
blank=False,
related_name="accounts",
)
def __str__(self):
return (
f"{self.user!s} "
f"{self.company!s} "
f"{self.role}"
)
def clean(self) -> None:
super().clean()
validator = CompanyAccountValidator(self)
validator()
@property
def user_name(self) -> str: # type: ignore
return self.user.full_name # type: ignore
@classmethod
def _create_fake(cls):
return cls.objects.create(
name="Mock CompanyAccount",
user=UserModel._create_fake(), # type: ignore
company=CompanyModel._create_fake() # type: ignore
)
class Meta: # type: ignore
db_table = "company_accounts"
verbose_name = _("Company Account")
verbose_name_plural = _("Company Accounts")
indexes = [
models.Index(fields=["user"], name="company_accounts_user_inx"),
models.Index(fields=["company"], name="company_accounts_company_inx")
]
unique_together = ["user", "company"]

View File

@@ -0,0 +1,184 @@
from typing import Self
from django.db import models
from django.core.validators import (
EmailValidator,
MinLengthValidator,
)
from django.utils.translation import gettext_lazy as _
from django.contrib.auth import get_user_model
from core.utils.base_model import UUIDPrimaryKeyBaseModel
from core.apps.banks.models import BankModel
from core.apps.companies.validators.companies import (
iik_validator,
iin_validator,
name_validator,
phone_validator,
pinfl_validator,
CompanyValidator,
)
UserModel = get_user_model()
class CompanyModel(UUIDPrimaryKeyBaseModel):
name = models.CharField(
_("name"),
max_length=512,
validators=[
name_validator,
MinLengthValidator(3)
],
null=False,
blank=False
)
pinfl_code = models.CharField(
_("PINFL code"),
validators=[
pinfl_validator,
],
null=True,
blank=True,
)
iin_code = models.CharField(
_("IIN code"),
validators=[iin_validator],
null=True,
blank=True
)
phone = models.CharField(
_("Phone Number"),
max_length=25,
unique=True,
validators=[
phone_validator,
],
null=False,
blank=False,
)
email = models.CharField(
_("Email Address"),
max_length=255,
validators=[
EmailValidator()
],
unique=True,
null=False,
blank=False,
)
iik_code = models.CharField(
_("IIK code"),
max_length=30,
validators=[
iik_validator
],
null=True, blank=True
)
bank = models.ForeignKey(
BankModel,
on_delete=models.SET_NULL,
null=True,
blank=True
)
legal_address = models.CharField(
_("Legal Address"),
max_length=512,
validators=[
MinLengthValidator(10)
],
null=True,
blank=True
)
real_address = models.CharField(
_("Real Address"),
max_length=512,
validators=[
MinLengthValidator(10)
],
null=True,
blank=True
)
logo = models.ImageField(
_("Logo"),
max_length=1024,
null=True,
blank=True
)
signature_authority_document = models.FileField(
_("Signature Authority Document"),
max_length=1024,
null=True,
blank=True
)
def __str__(self):
return self.name
@property
def company_code(self) -> str:
if self.pinfl_code is not None:
return f"PINFL code: {self.pinfl_code}"
else:
return f"IIN code: {self.iin_code}"
def clean(self):
super().clean()
validator = CompanyValidator(self)
validator()
@classmethod
def _create_fake(cls) -> Self:
return cls.objects.create(
name="mock LLC",
pinfl_code="12345678901234",
iin_code="12345678901234",
phone="+998901234567",
email="mock@example.com",
iik_code="UZS1234567890",
legal_address="Some legal address, City, Country",
real_address="Same as above",
bank=BankModel._create_fake() # type: ignore
)
class Meta: # type: ignore
db_table = "companies"
verbose_name = _("Company")
verbose_name_plural = _("Companies")
indexes = [
models.Index(
fields=["phone"],
name="companies_phone_inx"
),
models.Index(
fields=["email"],
name="companies_email_inx"
),
models.Index(
fields=["iin_code"],
name="companies_iin_code_inx"
),
models.Index(
fields=["pinfl_code"],
name="companies_pinfl_code_inx"
),
models.Index(
fields=["name"],
name="companies_name_inx"
),
]

View File

@@ -0,0 +1,68 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.core.validators import (
MaxLengthValidator,
MinLengthValidator,
)
from core.utils.base_model import UUIDPrimaryKeyBaseModel
from core.apps.companies.models.companies import CompanyModel
from core.apps.companies.validators.folders import (
CompanyFolderValidator
)
from core.apps.contracts.models import ContractModel
class CompanyFolderModel(UUIDPrimaryKeyBaseModel):
name = models.CharField(
_("name"),
max_length=150,
validators=[
MaxLengthValidator(150),
MinLengthValidator(3),
]
)
company = models.ForeignKey(
CompanyModel,
on_delete=models.PROTECT,
related_name="folders",
null=False,
blank=False
)
contracts = models.ManyToManyField( # type: ignore
ContractModel,
verbose_name=_("Contracts"),
related_name="folders",
)
def __str__(self):
return f"{self.name} {self.company!s}"
@classmethod
def _create_fake(cls):
return cls.objects.create(
name="mock",
company=CompanyModel._create_fake() # type: ignore
)
def clean(self) -> None:
super().clean()
validator = CompanyFolderValidator(self)
validator()
class Meta: # type: ignore
db_table = "company_folders"
verbose_name = _("Company Folder")
verbose_name_plural = _("Company Folders")
constraints = [
models.UniqueConstraint(
fields=["name", "company"],
name="company_folders_name_company_unique_constraint"
)
]

View File

@@ -0,0 +1,3 @@
from .accounts import * # noqa
from .companies import * # noqa
from .folders import * # noqa

View File

@@ -0,0 +1,12 @@
from rest_framework import permissions
class CompanyaccountPermission(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,23 @@
from rest_framework import permissions # type: ignore
from rest_framework.views import APIView # type: ignore
from rest_framework.request import HttpRequest # type: ignore
from core.apps.companies.models import (
CompanyAccountModel,
CompanyModel
)
class IsCompanyAccount(permissions.IsAuthenticated):
def has_object_permission( # type: ignore
self,
request: HttpRequest,
view: APIView,
obj: CompanyModel
) -> bool:
if request.user.is_staff:
return True
return CompanyAccountModel.objects.filter(
company=obj, user=request.user,
).exists()

View File

@@ -0,0 +1,12 @@
from rest_framework import permissions
class CompanyfolderPermission(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,3 @@
from .accounts import * # noqa
from .companies import * # noqa
from .folders import * # noqa

View File

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

View File

@@ -0,0 +1,36 @@
from rest_framework import serializers
from core.apps.companies.models import CompanyAccountModel
class BaseCompanyAccountSerializer(serializers.ModelSerializer):
class Meta:
model = CompanyAccountModel
fields = "__all__"
read_only_fields = (
"id",
"created_at",
"updated_at"
)
class ListCompanyAccountSerializer(BaseCompanyAccountSerializer):
class Meta(BaseCompanyAccountSerializer.Meta): ...
class RetrieveCompanyAccountSerializer(BaseCompanyAccountSerializer):
class Meta(BaseCompanyAccountSerializer.Meta): ...
class CreateCompanyAccountSerializer(BaseCompanyAccountSerializer):
class Meta(BaseCompanyAccountSerializer.Meta): ...
class UpdateCompanyAccountSerializer(BaseCompanyAccountSerializer):
class Meta(BaseCompanyAccountSerializer.Meta): ...
class DestroyCompanyAccountSerializer(BaseCompanyAccountSerializer):
class Meta(BaseCompanyAccountSerializer.Meta):
fields = ["id"]

View File

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

View File

@@ -0,0 +1,51 @@
from rest_framework import serializers
from core.apps.companies.models import CompanyModel
from core.apps.companies.serializers.accounts import (
CreateCompanyAccountSerializer,
)
from core.apps.companies.serializers.folders import (
CreateCompanyFolderSerializer
)
class BaseCompanySerializer(serializers.ModelSerializer):
class Meta:
model = CompanyModel
fields = "__all__"
read_only_fields = (
"id",
"created_at",
"updated_at"
)
class ListCompanySerializer(BaseCompanySerializer):
class Meta(BaseCompanySerializer.Meta):
fields = (
"id",
"name",
"phone",
"email",
"created_at",
"updated_at",
)
class RetrieveCompanySerializer(BaseCompanySerializer):
class Meta(BaseCompanySerializer.Meta): ...
class CreateCompanySerializer(BaseCompanySerializer):
class Meta(BaseCompanySerializer.Meta): ...
class UpdateCompanySerializer(BaseCompanySerializer):
class Meta(BaseCompanySerializer.Meta): ...
class DestroyCompanySerializer(BaseCompanySerializer):
class Meta(BaseCompanySerializer.Meta):
fields = ["id"]

View File

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

View File

@@ -0,0 +1,48 @@
from rest_framework import serializers # type: ignore
from core.apps.companies.models import CompanyFolderModel
class BaseCompanyFolderSerializer(serializers.ModelSerializer):
class Meta:
model = CompanyFolderModel
fields = "__all__"
read_only_fields = (
"id",
"created_at",
"updated_at"
)
class ListCompanyFolderSerializer(BaseCompanyFolderSerializer):
class Meta(BaseCompanyFolderSerializer.Meta): ...
class RetrieveCompanyFolderSerializer(BaseCompanyFolderSerializer):
class Meta(BaseCompanyFolderSerializer.Meta): ...
class CreateCompanyFolderSerializer(BaseCompanyFolderSerializer):
class Meta(BaseCompanyFolderSerializer.Meta): ...
class UpdateCompanyFolderSerializer(BaseCompanyFolderSerializer):
class Meta(BaseCompanyFolderSerializer.Meta): ...
class DestroyCompanyFolderSerializer(BaseCompanyFolderSerializer):
class Meta(BaseCompanyFolderSerializer.Meta):
fields = ["id"]
class CreateCompanyFolderFromCompanySerializer(CreateCompanyFolderSerializer):
class Meta(CreateCompanyFolderSerializer.Meta):
read_only_fields = (
*CreateCompanyFolderSerializer.Meta.read_only_fields,
"company",
)
def create(self, validated_data: dict[str, object]) -> Meta.model:
validated_data["company_id"] = self.context["company_id"]
return super().create(validated_data) # type: ignore

View File

@@ -0,0 +1,3 @@
from .accounts import * # noqa
from .companies import * # noqa
from .folders import * # noqa

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
from .test_accounts import * # noqa
from .test_companies import * # noqa
from .test_folders 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.companies.models import CompanyaccountModel
class CompanyaccountTest(TestCase):
def _create_data(self):
return CompanyaccountModel._create_fake()
def setUp(self):
self.client = APIClient()
self.instance = self._create_data()
self.urls = {
"list": reverse("CompanyAccount-list"),
"retrieve": reverse("CompanyAccount-detail", kwargs={"pk": self.instance.pk}),
"retrieve-not-found": reverse("CompanyAccount-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,47 @@
from django.test import TestCase
from django.urls import reverse
from rest_framework.test import APIClient
from core.apps.companies.models import CompanyModel
class CompanyTest(TestCase):
def _create_data(self):
return CompanyModel._create_fake()
def setUp(self):
self.client = APIClient()
self.instance = self._create_data()
self.urls = {
"list": reverse("Company-list"),
"retrieve": reverse("Company-detail", kwargs={"pk": self.instance.pk}),
"retrieve-not-found": reverse("Company-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,47 @@
from django.test import TestCase
from django.urls import reverse
from rest_framework.test import APIClient
from core.apps.companies.models import CompanyfolderModel
class CompanyfolderTest(TestCase):
def _create_data(self):
return CompanyfolderModel._create_fake()
def setUp(self):
self.client = APIClient()
self.instance = self._create_data()
self.urls = {
"list": reverse("CompanyFolder-list"),
"retrieve": reverse("CompanyFolder-detail", kwargs={"pk": self.instance.pk}),
"retrieve-not-found": reverse("CompanyFolder-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,3 @@
from .accounts import * # noqa
from .companies import * # noqa
from .folders import * # noqa

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views
router = DefaultRouter()
router.register(r"company-accounts", views.CompanyAccountView, "company-account")
router.register(r"company-folders", views.CompanyFolderView, "company-folders")
router.register(r"companies", views.CompanyView, "companies")
router.register(r"companies", views.CompanyFolderViewSet, "companies-folders")
router.register(r"companies", views.CompanyAccountViewSet, "companies-accounts")
router.register(r"companies", views.CompanyContractViewSet, "companies-contracts")
urlpatterns = [
path("", include(router.urls)),
]

View File

@@ -0,0 +1,3 @@
from .accounts import * # noqa
from .companies import * # noqa
from .folders import * # noqa

View File

@@ -0,0 +1,10 @@
# from django.core.exceptions import ValidationError
from django_core.models.base import AbstractBaseModel # type: ignore
class CompanyAccountValidator:
def __init__(self, instance: AbstractBaseModel):
self.instance = instance
def __call__(self):
return True

View File

@@ -0,0 +1,62 @@
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django_core.models.base import AbstractBaseModel # type: ignore
from django.utils.translation import gettext_lazy as _
phone_validator = RegexValidator(
regex=r'^\+?[1-9]\d{1,14}$',
message=_(
"Enter a valid international phone number "
"(E.164 format, e.g., +14155552671)."
)
)
iin_validator = RegexValidator(
regex=r'^\d{14}$',
message=_("IIN code must be exactly 14 digits.")
)
pinfl_validator = RegexValidator(
regex=r'^\d{14}$',
message=_("PINFL code must be exactly 14 digits.")
)
iik_validator = RegexValidator(
regex=r'^[A-Z0-9]{10,30}$',
message=_(
"IIK code must be alphanumeric and " \
"between 10 and 30 characters."
),
)
name_validator = RegexValidator(
regex=r'^[A-Za-z0-9\s\.\-\,\&]+$',
message=_("Company name contains invalid characters.")
)
class CompanyValidator:
def __init__(self, instance: AbstractBaseModel):
self.instance = instance
def __call__(self):
if (
self.instance.iin_code is None # type: ignore
and self.instance.pinfl_code is None # type: ignore
):
raise ValidationError(_(
"Either IIN code or PINFL " \
"code must be provided."
))
if (
self.instance.iin_code is not None # type: ignore
and self.instance.pinfl_code is not None # type: ignore
):
raise ValidationError(_(
"One of IIN code and PINFL "
"code must be None"
))

View File

@@ -0,0 +1,10 @@
# from django.core.exceptions import ValidationError
from django_core.models.base import AbstractBaseModel # type: ignore
class CompanyFolderValidator:
def __init__(self, instance: AbstractBaseModel):
self.instance = instance
def __call__(self):
return

View File

@@ -0,0 +1,3 @@
from .accounts import * # noqa
from .companies import * # noqa
from .folders import * # noqa

View File

@@ -0,0 +1,35 @@
from django_core.mixins import BaseViewSetMixin
from drf_spectacular.utils import extend_schema
from rest_framework.permissions import IsAdminUser, AllowAny
from rest_framework.viewsets import ModelViewSet
from core.apps.companies.models import CompanyAccountModel
from core.apps.companies.serializers.accounts import (
CreateCompanyAccountSerializer,
ListCompanyAccountSerializer,
RetrieveCompanyAccountSerializer,
UpdateCompanyAccountSerializer,
DestroyCompanyAccountSerializer,
)
@extend_schema(tags=["CompanyAccount"])
class CompanyAccountView(BaseViewSetMixin, ModelViewSet):
queryset = CompanyAccountModel.objects.all()
serializer_class = ListCompanyAccountSerializer
permission_classes = [AllowAny]
action_permission_classes = {
"list": [IsAdminUser],
"retrieve": [IsAdminUser],
"create": [IsAdminUser],
"update": [IsAdminUser],
"destroy": [IsAdminUser],
}
action_serializer_class = {
"list": ListCompanyAccountSerializer,
"retrieve": RetrieveCompanyAccountSerializer,
"create": CreateCompanyAccountSerializer,
"update": UpdateCompanyAccountSerializer,
"destroy": DestroyCompanyAccountSerializer,
}

View File

@@ -0,0 +1,189 @@
import uuid
from django_core.mixins import BaseViewSetMixin # type: ignore
from drf_spectacular.utils import extend_schema
from rest_framework.decorators import action # type: ignore
from rest_framework.permissions import AllowAny, IsAdminUser # type: ignore
from rest_framework.viewsets import ( # type: ignore
ModelViewSet,
GenericViewSet
)
from rest_framework.request import HttpRequest # type: ignore
from rest_framework.response import Response # type: ignore
from rest_framework import status # type: ignore
from rest_framework.generics import get_object_or_404 # type: ignore
from django.contrib.auth import get_user_model
from core.apps.companies.permissions import IsCompanyAccount
from core.apps.companies.models import (
CompanyModel,
CompanyFolderModel,
CompanyAccountModel
)
from core.apps.companies.serializers import (
CreateCompanySerializer,
ListCompanySerializer,
RetrieveCompanySerializer,
UpdateCompanySerializer,
DestroyCompanySerializer,
RetrieveCompanyFolderSerializer,
RetrieveCompanyAccountSerializer,
CreateCompanyFolderSerializer,
BaseCompanyAccountSerializer,
CreateCompanyFolderFromCompanySerializer
)
from core.apps.contracts.serializers import (
RetrieveContractSerializer,
BaseContractSerializer
)
from core.apps.contracts.models import (
ContractModel,
)
UserModel = get_user_model()
@extend_schema(tags=["Company"])
class CompanyView(BaseViewSetMixin, ModelViewSet):
queryset = CompanyModel.objects.all()
serializer_class = ListCompanySerializer
permission_classes = [AllowAny]
action_permission_classes = {
"list": [IsAdminUser],
"retrieve": [IsAdminUser],
"create": [IsAdminUser],
"update": [IsAdminUser],
"destroy": [IsAdminUser],
}
action_serializer_class = { # type: ignore
"list": ListCompanySerializer,
"retrieve": RetrieveCompanySerializer,
"create": CreateCompanySerializer,
"update": UpdateCompanySerializer,
"destroy": DestroyCompanySerializer,
}
class CompanyContractViewSet(BaseViewSetMixin, GenericViewSet):
queryset = CompanyModel.objects.all()
permission_classes = [AllowAny]
serializer_class = BaseContractSerializer
action_permission_classes = {
"list_contract": [IsCompanyAccount]
}
action_serializer_class = {
"list_contract": RetrieveContractSerializer
}
#! TODO: status should be added.
@extend_schema(
summary="Company Contracts",
description="Get List Company Contracts"
)
@action(methods=["GET"], detail=True, url_path="contracts")
def list_contract(
self,
request: HttpRequest,
*args: object,
**kwargs: object,
) -> Response:
company = self.get_object()
contracts = ContractModel.objects.filter(
owners__legal_entity__phone=company.phone,
).distinct()
folders_param = request.data.get("folders")
if folders_param:
folder_ids = folders_param.split(",")
contracts = contracts.filter(folders__id__in=folder_ids)
serializer = self.get_serializer(instance=contracts, many=True) # type: ignore
return Response(serializer.data, status=status.HTTP_200_OK)
class CompanyAccountViewSet(BaseViewSetMixin, GenericViewSet):
queryset = CompanyModel.objects.all()
serializer_class = BaseCompanyAccountSerializer
permission_classes = [AllowAny]
action_permission_classes = {
"list_account": [IsCompanyAccount]
}
action_serializer_class = {
"list_account": RetrieveCompanyAccountSerializer
}
@extend_schema(
summary="List company accounts",
description="List Company Accounts"
)
@action(url_path="accounts", detail=True, methods=["GET"])
def list_account(
self,
request: HttpRequest,
pk: uuid.UUID,
*args: object,
**kwargs: object,
) -> Response:
company = self.get_object()
accounts = CompanyAccountModel.objects.filter(company=company)
ser = self.get_serializer(instance=accounts, many=True) # type: ignore
return Response(data=ser.data, status=status.HTTP_200_OK)
class CompanyFolderViewSet(BaseViewSetMixin, GenericViewSet):
queryset = CompanyModel.objects.all()
permission_classes = [AllowAny]
# serializer_class = BaseCompanyFolderSerializer
action_permission_classes = {
"list_folder": [IsCompanyAccount],
"create_folder": [IsCompanyAccount],
}
action_serializer_class = { # type: ignore
"list_folder": RetrieveCompanyFolderSerializer,
"create_folder": CreateCompanyFolderFromCompanySerializer,
}
@extend_schema(
summary="List Company Folders",
description="List Company Folders"
)
@action(methods=["GET"], detail=True, url_path="folders")
def list_folder(
self,
request: HttpRequest,
*args: object,
**kwargs: object,
) -> Response:
company = self.get_object()
folders = CompanyFolderModel.objects.filter(company=company)
ser = self.get_serializer(instance=folders, many=True, context={"company_id": company.pk}) # type: ignore
return Response(data=ser.data, status=status.HTTP_200_OK)
@extend_schema(
summary="Create Folder for company",
description="Create Folder for company",
)
@action(url_path="folders", detail=True, methods=["POST"])
def create_folder(
self,
request: HttpRequest,
pk: uuid.UUID,
*args: object,
**kwargs: object,
) -> Response:
company = self.get_object()
data = request.data.copy() | dict(company=company.id) # type: ignore
ser = CreateCompanyFolderSerializer(data=data) # type: ignore
ser.is_valid(raise_exception=True)
return Response(data=ser.data, status=status.HTTP_201_CREATED)

View File

@@ -0,0 +1,35 @@
from django_core.mixins import BaseViewSetMixin
from drf_spectacular.utils import extend_schema
from rest_framework.permissions import AllowAny, IsAdminUser
from rest_framework.viewsets import ModelViewSet
from core.apps.companies.models import CompanyFolderModel
from core.apps.companies.serializers.folders import (
CreateCompanyFolderSerializer,
ListCompanyFolderSerializer,
RetrieveCompanyFolderSerializer,
UpdateCompanyFolderSerializer,
DestroyCompanyFolderSerializer
)
@extend_schema(tags=["CompanyFolder"])
class CompanyFolderView(BaseViewSetMixin, ModelViewSet):
queryset = CompanyFolderModel.objects.all()
serializer_class = ListCompanyFolderSerializer
permission_classes = [AllowAny]
action_permission_classes = {
"list": [IsAdminUser],
"retrieve": [IsAdminUser],
"create": [IsAdminUser],
"update": [IsAdminUser],
"destroy": [IsAdminUser],
}
action_serializer_class = {
"list": ListCompanyFolderSerializer,
"retrieve": RetrieveCompanyFolderSerializer,
"create": CreateCompanyFolderSerializer,
"update": UpdateCompanyFolderSerializer,
"destroy": DestroyCompanyFolderSerializer,
}