initial commit
This commit is contained in:
0
core/apps/companies/__init__.py
Normal file
0
core/apps/companies/__init__.py
Normal file
3
core/apps/companies/admin/__init__.py
Normal file
3
core/apps/companies/admin/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .accounts import * # noqa
|
||||
from .companies import * # noqa
|
||||
from .folders import * # noqa
|
||||
27
core/apps/companies/admin/accounts.py
Normal file
27
core/apps/companies/admin/accounts.py
Normal 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",
|
||||
)
|
||||
32
core/apps/companies/admin/companies.py
Normal file
32
core/apps/companies/admin/companies.py
Normal 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",
|
||||
)
|
||||
24
core/apps/companies/admin/folders.py
Normal file
24
core/apps/companies/admin/folders.py
Normal 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",
|
||||
)
|
||||
6
core/apps/companies/apps.py
Normal file
6
core/apps/companies/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ModuleConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "core.apps.companies"
|
||||
3
core/apps/companies/filters/__init__.py
Normal file
3
core/apps/companies/filters/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .accounts import * # noqa
|
||||
from .companies import * # noqa
|
||||
from .folders import * # noqa
|
||||
13
core/apps/companies/filters/accounts.py
Normal file
13
core/apps/companies/filters/accounts.py
Normal 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",
|
||||
]
|
||||
13
core/apps/companies/filters/companies.py
Normal file
13
core/apps/companies/filters/companies.py
Normal 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",
|
||||
]
|
||||
13
core/apps/companies/filters/folders.py
Normal file
13
core/apps/companies/filters/folders.py
Normal 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",
|
||||
]
|
||||
3
core/apps/companies/forms/__init__.py
Normal file
3
core/apps/companies/forms/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .accounts import * # noqa
|
||||
from .companies import * # noqa
|
||||
from .folders import * # noqa
|
||||
10
core/apps/companies/forms/accounts.py
Normal file
10
core/apps/companies/forms/accounts.py
Normal 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__"
|
||||
10
core/apps/companies/forms/companies.py
Normal file
10
core/apps/companies/forms/companies.py
Normal 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__"
|
||||
10
core/apps/companies/forms/folders.py
Normal file
10
core/apps/companies/forms/folders.py
Normal 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__"
|
||||
257
core/apps/companies/migrations/0001_initial.py
Normal file
257
core/apps/companies/migrations/0001_initial.py
Normal 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")},
|
||||
),
|
||||
]
|
||||
@@ -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"),
|
||||
),
|
||||
]
|
||||
@@ -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"
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -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"
|
||||
),
|
||||
),
|
||||
]
|
||||
0
core/apps/companies/migrations/__init__.py
Normal file
0
core/apps/companies/migrations/__init__.py
Normal file
3
core/apps/companies/models/__init__.py
Normal file
3
core/apps/companies/models/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .accounts import * # noqa
|
||||
from .companies import * # noqa
|
||||
from .folders import * # noqa
|
||||
77
core/apps/companies/models/accounts.py
Normal file
77
core/apps/companies/models/accounts.py
Normal 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"]
|
||||
184
core/apps/companies/models/companies.py
Normal file
184
core/apps/companies/models/companies.py
Normal 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"
|
||||
),
|
||||
]
|
||||
68
core/apps/companies/models/folders.py
Normal file
68
core/apps/companies/models/folders.py
Normal 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"
|
||||
)
|
||||
]
|
||||
3
core/apps/companies/permissions/__init__.py
Normal file
3
core/apps/companies/permissions/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .accounts import * # noqa
|
||||
from .companies import * # noqa
|
||||
from .folders import * # noqa
|
||||
12
core/apps/companies/permissions/accounts.py
Normal file
12
core/apps/companies/permissions/accounts.py
Normal 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
|
||||
23
core/apps/companies/permissions/companies.py
Normal file
23
core/apps/companies/permissions/companies.py
Normal 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()
|
||||
12
core/apps/companies/permissions/folders.py
Normal file
12
core/apps/companies/permissions/folders.py
Normal 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
|
||||
3
core/apps/companies/serializers/__init__.py
Normal file
3
core/apps/companies/serializers/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .accounts import * # noqa
|
||||
from .companies import * # noqa
|
||||
from .folders import * # noqa
|
||||
1
core/apps/companies/serializers/accounts/__init__.py
Normal file
1
core/apps/companies/serializers/accounts/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .company_accounts import * # noqa
|
||||
36
core/apps/companies/serializers/accounts/company_accounts.py
Normal file
36
core/apps/companies/serializers/accounts/company_accounts.py
Normal 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"]
|
||||
|
||||
1
core/apps/companies/serializers/companies/__init__.py
Normal file
1
core/apps/companies/serializers/companies/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .companies import * # noqa
|
||||
51
core/apps/companies/serializers/companies/companies.py
Normal file
51
core/apps/companies/serializers/companies/companies.py
Normal 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"]
|
||||
1
core/apps/companies/serializers/folders/__init__.py
Normal file
1
core/apps/companies/serializers/folders/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .company_folders import * # noqa
|
||||
48
core/apps/companies/serializers/folders/company_folders.py
Normal file
48
core/apps/companies/serializers/folders/company_folders.py
Normal 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
|
||||
|
||||
3
core/apps/companies/signals/__init__.py
Normal file
3
core/apps/companies/signals/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .accounts import * # noqa
|
||||
from .companies import * # noqa
|
||||
from .folders import * # noqa
|
||||
8
core/apps/companies/signals/accounts.py
Normal file
8
core/apps/companies/signals/accounts.py
Normal 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): ...
|
||||
8
core/apps/companies/signals/companies.py
Normal file
8
core/apps/companies/signals/companies.py
Normal 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): ...
|
||||
8
core/apps/companies/signals/folders.py
Normal file
8
core/apps/companies/signals/folders.py
Normal 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): ...
|
||||
3
core/apps/companies/tests/__init__.py
Normal file
3
core/apps/companies/tests/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .test_accounts import * # noqa
|
||||
from .test_companies import * # noqa
|
||||
from .test_folders import * # noqa
|
||||
47
core/apps/companies/tests/test_accounts.py
Normal file
47
core/apps/companies/tests/test_accounts.py
Normal 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)
|
||||
47
core/apps/companies/tests/test_companies.py
Normal file
47
core/apps/companies/tests/test_companies.py
Normal 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)
|
||||
47
core/apps/companies/tests/test_folders.py
Normal file
47
core/apps/companies/tests/test_folders.py
Normal 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)
|
||||
3
core/apps/companies/translation/__init__.py
Normal file
3
core/apps/companies/translation/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .accounts import * # noqa
|
||||
from .companies import * # noqa
|
||||
from .folders import * # noqa
|
||||
8
core/apps/companies/translation/accounts.py
Normal file
8
core/apps/companies/translation/accounts.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from modeltranslation.translator import TranslationOptions, register
|
||||
|
||||
from core.apps.companies.models import CompanyaccountModel
|
||||
|
||||
|
||||
@register(CompanyaccountModel)
|
||||
class CompanyaccountTranslation(TranslationOptions):
|
||||
fields = []
|
||||
8
core/apps/companies/translation/companies.py
Normal file
8
core/apps/companies/translation/companies.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from modeltranslation.translator import TranslationOptions, register
|
||||
|
||||
from core.apps.companies.models import CompanyModel
|
||||
|
||||
|
||||
@register(CompanyModel)
|
||||
class CompanyTranslation(TranslationOptions):
|
||||
fields = []
|
||||
8
core/apps/companies/translation/folders.py
Normal file
8
core/apps/companies/translation/folders.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from modeltranslation.translator import TranslationOptions, register
|
||||
|
||||
from core.apps.companies.models import CompanyfolderModel
|
||||
|
||||
|
||||
@register(CompanyfolderModel)
|
||||
class CompanyfolderTranslation(TranslationOptions):
|
||||
fields = []
|
||||
18
core/apps/companies/urls.py
Normal file
18
core/apps/companies/urls.py
Normal 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)),
|
||||
]
|
||||
3
core/apps/companies/validators/__init__.py
Normal file
3
core/apps/companies/validators/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .accounts import * # noqa
|
||||
from .companies import * # noqa
|
||||
from .folders import * # noqa
|
||||
10
core/apps/companies/validators/accounts.py
Normal file
10
core/apps/companies/validators/accounts.py
Normal 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
|
||||
62
core/apps/companies/validators/companies.py
Normal file
62
core/apps/companies/validators/companies.py
Normal 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"
|
||||
))
|
||||
10
core/apps/companies/validators/folders.py
Normal file
10
core/apps/companies/validators/folders.py
Normal 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
|
||||
3
core/apps/companies/views/__init__.py
Normal file
3
core/apps/companies/views/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .accounts import * # noqa
|
||||
from .companies import * # noqa
|
||||
from .folders import * # noqa
|
||||
35
core/apps/companies/views/accounts.py
Normal file
35
core/apps/companies/views/accounts.py
Normal 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,
|
||||
}
|
||||
189
core/apps/companies/views/companies.py
Normal file
189
core/apps/companies/views/companies.py
Normal 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)
|
||||
35
core/apps/companies/views/folders.py
Normal file
35
core/apps/companies/views/folders.py
Normal 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,
|
||||
}
|
||||
Reference in New Issue
Block a user