behruz #134
@@ -15,6 +15,7 @@ APPS = [
|
|||||||
"django_core",
|
"django_core",
|
||||||
"core.apps.accounts.apps.AccountsConfig",
|
"core.apps.accounts.apps.AccountsConfig",
|
||||||
'core.apps.tasks.apps.TasksConfig',
|
'core.apps.tasks.apps.TasksConfig',
|
||||||
|
'core.apps.documents.apps.DocumentsConfig',
|
||||||
]
|
]
|
||||||
|
|
||||||
if env.bool("SILK_ENABLED", False):
|
if env.bool("SILK_ENABLED", False):
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ urlpatterns = [
|
|||||||
path("api/v1/", include("core.apps.payment.urls")),
|
path("api/v1/", include("core.apps.payment.urls")),
|
||||||
path("api/v1/", include("core.apps.chat.urls")),
|
path("api/v1/", include("core.apps.chat.urls")),
|
||||||
path("api/v1/tasks/", include("core.apps.tasks.urls")),
|
path("api/v1/tasks/", include("core.apps.tasks.urls")),
|
||||||
|
path("api/v1/documents/", include("core.apps.documents.urls")),
|
||||||
]
|
]
|
||||||
urlpatterns += [
|
urlpatterns += [
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
|
|||||||
0
core/apps/documents/__init__.py
Normal file
0
core/apps/documents/__init__.py
Normal file
5
core/apps/documents/apps.py
Normal file
5
core/apps/documents/apps.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class DocumentsConfig(AppConfig):
|
||||||
|
name = "core.apps.documents"
|
||||||
0
core/apps/documents/migrations/__init__.py
Normal file
0
core/apps/documents/migrations/__init__.py
Normal file
7
core/apps/documents/urls.py
Normal file
7
core/apps/documents/urls.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from core.apps.documents.views.contract import ValuationReportPDFView
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('generate-contract-pdf/<int:pk>/', ValuationReportPDFView.as_view(), name='generate_contract_pdf'),
|
||||||
|
]
|
||||||
387
core/apps/documents/views/contract.py
Normal file
387
core/apps/documents/views/contract.py
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
from datetime import date
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import status
|
||||||
|
from weasyprint import HTML
|
||||||
|
|
||||||
|
from core.apps.evaluation.models import AutoEvaluationModel
|
||||||
|
|
||||||
|
|
||||||
|
UZ_MONTHS = {
|
||||||
|
1: "yanvar", 2: "fevral", 3: "mart", 4: "aprel",
|
||||||
|
5: "may", 6: "iyun", 7: "iyul", 8: "avgust",
|
||||||
|
9: "sentabr", 10: "oktabr", 11: "noyabr", 12: "dekabr",
|
||||||
|
}
|
||||||
|
|
||||||
|
UZ_ONES = [
|
||||||
|
"", "bir", "ikki", "uch", "to'rt", "besh",
|
||||||
|
"olti", "yetti", "sakkiz", "to'qqiz",
|
||||||
|
]
|
||||||
|
UZ_TENS = [
|
||||||
|
"", "o'n", "yigirma", "o'ttiz", "qirq", "ellik",
|
||||||
|
"oltmish", "yetmish", "sakson", "to'qson",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _format_currency(value):
|
||||||
|
if value is None:
|
||||||
|
return "0"
|
||||||
|
try:
|
||||||
|
int_val = int(Decimal(value))
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return "0"
|
||||||
|
return f"{int_val:,}".replace(",", " ")
|
||||||
|
|
||||||
|
|
||||||
|
def _format_date(value):
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
return value.strftime("%d.%m.%Y")
|
||||||
|
|
||||||
|
|
||||||
|
def _three_digit_words(num):
|
||||||
|
if num == 0:
|
||||||
|
return ""
|
||||||
|
words = []
|
||||||
|
hundreds = num // 100
|
||||||
|
rest = num % 100
|
||||||
|
if hundreds:
|
||||||
|
if hundreds == 1:
|
||||||
|
words.append("bir yuz")
|
||||||
|
else:
|
||||||
|
words.append(f"{UZ_ONES[hundreds]} yuz")
|
||||||
|
tens = rest // 10
|
||||||
|
ones = rest % 10
|
||||||
|
if tens:
|
||||||
|
words.append(UZ_TENS[tens])
|
||||||
|
if ones:
|
||||||
|
words.append(UZ_ONES[ones])
|
||||||
|
return " ".join(words)
|
||||||
|
|
||||||
|
|
||||||
|
def _number_to_uzbek_words(value):
|
||||||
|
if value is None:
|
||||||
|
return ""
|
||||||
|
try:
|
||||||
|
num = int(Decimal(value))
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return ""
|
||||||
|
if num == 0:
|
||||||
|
return "nol"
|
||||||
|
|
||||||
|
parts = []
|
||||||
|
billions = num // 1_000_000_000
|
||||||
|
millions = (num % 1_000_000_000) // 1_000_000
|
||||||
|
thousands = (num % 1_000_000) // 1_000
|
||||||
|
rest = num % 1_000
|
||||||
|
|
||||||
|
if billions:
|
||||||
|
parts.append(f"{_three_digit_words(billions)} milliard")
|
||||||
|
if millions:
|
||||||
|
parts.append(f"{_three_digit_words(millions)} million")
|
||||||
|
if thousands:
|
||||||
|
parts.append(f"{_three_digit_words(thousands)} ming")
|
||||||
|
if rest:
|
||||||
|
parts.append(_three_digit_words(rest))
|
||||||
|
|
||||||
|
text = " ".join(parts).strip()
|
||||||
|
return text[0].upper() + text[1:] if text else ""
|
||||||
|
|
||||||
|
|
||||||
|
class ValuationReportPDFView(APIView):
|
||||||
|
"""
|
||||||
|
Baholash hisobotini PDF formatida yuklab olish uchun API.
|
||||||
|
|
||||||
|
GET /api/documents/generate-contract-pdf/<pk>/
|
||||||
|
pk — AutoEvaluationModel id si.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get(self, request, pk, *args, **kwargs):
|
||||||
|
return self._generate_pdf(request, pk)
|
||||||
|
|
||||||
|
def post(self, request, pk, *args, **kwargs):
|
||||||
|
return self._generate_pdf(request, pk)
|
||||||
|
|
||||||
|
def _generate_pdf(self, request, pk):
|
||||||
|
auto_evaluation = get_object_or_404(
|
||||||
|
AutoEvaluationModel.objects.select_related(
|
||||||
|
"vehicle",
|
||||||
|
"vehicle__brand",
|
||||||
|
"vehicle__model",
|
||||||
|
"vehicle__color",
|
||||||
|
"vehicle__fuel_type",
|
||||||
|
"vehicle__body_type",
|
||||||
|
"valuation",
|
||||||
|
"valuation__customer",
|
||||||
|
"valuation__property_owner",
|
||||||
|
),
|
||||||
|
pk=pk,
|
||||||
|
)
|
||||||
|
|
||||||
|
context = self._build_context(auto_evaluation)
|
||||||
|
|
||||||
|
html_string = render_to_string("documents/contract.html", context)
|
||||||
|
base_url = request.build_absolute_uri("/")
|
||||||
|
|
||||||
|
try:
|
||||||
|
pdf_file = HTML(string=html_string, base_url=base_url).write_pdf()
|
||||||
|
except Exception as e:
|
||||||
|
return Response(
|
||||||
|
{"error": f"PDF yaratishda xatolik: {str(e)}"},
|
||||||
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
)
|
||||||
|
|
||||||
|
report_number = context["report"]["number"]
|
||||||
|
filename = f"baholash_hisoboti_{report_number}.pdf"
|
||||||
|
|
||||||
|
response = HttpResponse(pdf_file, content_type="application/pdf")
|
||||||
|
response["Content-Disposition"] = f'attachment; filename="{filename}"'
|
||||||
|
response["Content-Length"] = len(pdf_file)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def _build_context(self, auto):
|
||||||
|
vehicle = auto.vehicle
|
||||||
|
valuation = auto.valuation
|
||||||
|
customer = valuation.customer if valuation else None
|
||||||
|
owner = valuation.property_owner if valuation and valuation.property_owner else customer
|
||||||
|
report = getattr(valuation, "report", None) if valuation else None
|
||||||
|
|
||||||
|
report_date = (
|
||||||
|
auto.rate_report_date
|
||||||
|
or auto.contract_date
|
||||||
|
or (report.created_at.date() if report else None)
|
||||||
|
or date.today()
|
||||||
|
)
|
||||||
|
valuation_date = auto.rate_date or report_date
|
||||||
|
inspection_date = auto.object_inspection_date or report_date
|
||||||
|
|
||||||
|
report_number = (
|
||||||
|
(report.report_number if report else None)
|
||||||
|
or auto.registration_number
|
||||||
|
or (valuation.conclusion_number if valuation else None)
|
||||||
|
or f"{auto.pk}/{report_date.year}"
|
||||||
|
)
|
||||||
|
|
||||||
|
final_value = None
|
||||||
|
if report and report.final_value is not None:
|
||||||
|
final_value = report.final_value
|
||||||
|
elif valuation and valuation.final_price is not None:
|
||||||
|
final_value = valuation.final_price
|
||||||
|
elif valuation and valuation.estimated_price is not None:
|
||||||
|
final_value = valuation.estimated_price
|
||||||
|
|
||||||
|
market_value_formatted = (
|
||||||
|
f"{_format_currency(final_value)} so'm" if final_value is not None else "0 so'm"
|
||||||
|
)
|
||||||
|
market_value_words = (
|
||||||
|
f"{_number_to_uzbek_words(final_value)} so'm"
|
||||||
|
if final_value is not None
|
||||||
|
else ""
|
||||||
|
)
|
||||||
|
|
||||||
|
cost_final = final_value
|
||||||
|
comparative_final = final_value
|
||||||
|
|
||||||
|
brand_name = ""
|
||||||
|
model_name = ""
|
||||||
|
if vehicle:
|
||||||
|
brand_name = vehicle.brand.name if vehicle.brand else ""
|
||||||
|
model_name = vehicle.model.name if vehicle.model else ""
|
||||||
|
if not brand_name:
|
||||||
|
brand_name = auto.car_brand or ""
|
||||||
|
if not model_name:
|
||||||
|
model_name = auto.car_model or ""
|
||||||
|
full_brand = f"{brand_name} {model_name}".strip()
|
||||||
|
|
||||||
|
plate_number = (vehicle.license_plate if vehicle else None) or auto.car_number or ""
|
||||||
|
manufacture_year = ""
|
||||||
|
if vehicle and vehicle.manufacture_year:
|
||||||
|
manufacture_year = str(vehicle.manufacture_year)
|
||||||
|
elif auto.manufacture_year:
|
||||||
|
manufacture_year = str(auto.manufacture_year)
|
||||||
|
|
||||||
|
production_date = f"{manufacture_year}-yil" if manufacture_year else ""
|
||||||
|
engine_number = (vehicle.engine_number if vehicle else None) or auto.car_dvigatel_number or ""
|
||||||
|
body_number = vehicle.vin_number if vehicle and vehicle.vin_number else ""
|
||||||
|
color_value = ""
|
||||||
|
if vehicle and vehicle.color:
|
||||||
|
color_value = vehicle.color.name
|
||||||
|
elif auto.car_color:
|
||||||
|
color_value = auto.car_color
|
||||||
|
fuel_type_value = ""
|
||||||
|
if vehicle and vehicle.fuel_type:
|
||||||
|
fuel_type_value = vehicle.fuel_type.name
|
||||||
|
|
||||||
|
tech_passport_value = ""
|
||||||
|
if vehicle and (vehicle.tech_passport_series or vehicle.tech_passport_number):
|
||||||
|
tech_passport_value = (
|
||||||
|
f"{vehicle.tech_passport_series or ''} № {vehicle.tech_passport_number or ''}"
|
||||||
|
).strip()
|
||||||
|
elif auto.tex_passport_serie_num:
|
||||||
|
tech_passport_value = auto.tex_passport_serie_num
|
||||||
|
|
||||||
|
customer_ctx = self._customer_context(customer)
|
||||||
|
owner_ctx = self._owner_context(owner)
|
||||||
|
if not owner_ctx["name"]:
|
||||||
|
owner_ctx = customer_ctx
|
||||||
|
|
||||||
|
contract_ctx = self._contract_context(auto, report_date)
|
||||||
|
|
||||||
|
director_name = customer.director_name if customer and customer.director_name else "—"
|
||||||
|
|
||||||
|
ctx = {
|
||||||
|
"logo_url": "",
|
||||||
|
"report": {
|
||||||
|
"number": report_number,
|
||||||
|
"date": _format_date(report_date),
|
||||||
|
"valuation_date": _format_date(valuation_date),
|
||||||
|
"inspection_date": _format_date(inspection_date),
|
||||||
|
"year": str(report_date.year),
|
||||||
|
"market_value_formatted": market_value_formatted,
|
||||||
|
"market_value_words": market_value_words,
|
||||||
|
},
|
||||||
|
"vehicle": {
|
||||||
|
"brand": full_brand,
|
||||||
|
"plate_number": plate_number,
|
||||||
|
"production_date": production_date,
|
||||||
|
"production_year": manufacture_year,
|
||||||
|
"type": auto.get_object_type_display() if auto.object_type else "",
|
||||||
|
"engine_number": engine_number,
|
||||||
|
"body_number": body_number,
|
||||||
|
"chassis_number": body_number,
|
||||||
|
"color": color_value,
|
||||||
|
"tech_passport": tech_passport_value,
|
||||||
|
"fuel_type": fuel_type_value,
|
||||||
|
"engine_power": "",
|
||||||
|
"full_weight": "",
|
||||||
|
"empty_weight": "",
|
||||||
|
},
|
||||||
|
"customer": customer_ctx,
|
||||||
|
"owner": owner_ctx,
|
||||||
|
"contract": contract_ctx,
|
||||||
|
"company": {
|
||||||
|
"director": director_name,
|
||||||
|
},
|
||||||
|
"rates": {
|
||||||
|
"rur": "",
|
||||||
|
"usd": "",
|
||||||
|
"eur": "",
|
||||||
|
},
|
||||||
|
"inspection": {
|
||||||
|
"tires": "",
|
||||||
|
"engine": "",
|
||||||
|
"chassis": "",
|
||||||
|
"transmission": "",
|
||||||
|
"body": "",
|
||||||
|
},
|
||||||
|
"cost": {
|
||||||
|
"engine_volume": "",
|
||||||
|
"factory_price": _format_currency(cost_final),
|
||||||
|
"replacement_value": _format_currency(cost_final),
|
||||||
|
"wear_percent": "",
|
||||||
|
"final_value": _format_currency(cost_final),
|
||||||
|
"final_value_words": _number_to_uzbek_words(cost_final) + (" so'm" if cost_final else ""),
|
||||||
|
},
|
||||||
|
"comparative": {
|
||||||
|
"final_value": _format_currency(comparative_final),
|
||||||
|
"final_value_usd": "",
|
||||||
|
"final_value_words": _number_to_uzbek_words(comparative_final) + (" so'm" if comparative_final else ""),
|
||||||
|
},
|
||||||
|
"approach": {
|
||||||
|
"cost": {
|
||||||
|
"value": _format_currency(cost_final),
|
||||||
|
"weight": "30%",
|
||||||
|
"weighted": "",
|
||||||
|
},
|
||||||
|
"comparative": {
|
||||||
|
"value": _format_currency(comparative_final),
|
||||||
|
"weight": "70%",
|
||||||
|
"weighted": "",
|
||||||
|
},
|
||||||
|
"weighted_total": _format_currency(final_value),
|
||||||
|
},
|
||||||
|
"analog_1": self._empty_analog(),
|
||||||
|
"analog_2": self._empty_analog(),
|
||||||
|
"analog_3": self._empty_analog(),
|
||||||
|
}
|
||||||
|
return ctx
|
||||||
|
|
||||||
|
def _customer_context(self, customer):
|
||||||
|
empty = {
|
||||||
|
"name": "",
|
||||||
|
"address": "",
|
||||||
|
"phone": "",
|
||||||
|
"tin": "",
|
||||||
|
"account": "",
|
||||||
|
"bank": "",
|
||||||
|
"mfo": "",
|
||||||
|
}
|
||||||
|
if not customer:
|
||||||
|
return empty
|
||||||
|
if customer.customer_type == "legal":
|
||||||
|
return {
|
||||||
|
"name": customer.org_name or "",
|
||||||
|
"address": customer.org_address or "",
|
||||||
|
"phone": "",
|
||||||
|
"tin": customer.inn or "",
|
||||||
|
"account": customer.bank_account or "",
|
||||||
|
"bank": "",
|
||||||
|
"mfo": customer.mfo or "",
|
||||||
|
}
|
||||||
|
full_name = " ".join(
|
||||||
|
filter(None, [customer.last_name, customer.first_name, customer.middle_name])
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"name": full_name,
|
||||||
|
"address": customer.address or "",
|
||||||
|
"phone": "",
|
||||||
|
"tin": customer.jshshir or "",
|
||||||
|
"account": "",
|
||||||
|
"bank": "",
|
||||||
|
"mfo": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
def _owner_context(self, owner):
|
||||||
|
empty = {"name": "", "address": ""}
|
||||||
|
if not owner:
|
||||||
|
return empty
|
||||||
|
type_field = getattr(owner, "owner_type", None) or getattr(owner, "customer_type", None)
|
||||||
|
if type_field == "legal":
|
||||||
|
return {
|
||||||
|
"name": owner.org_name or "",
|
||||||
|
"address": owner.org_address or "",
|
||||||
|
}
|
||||||
|
full_name = " ".join(
|
||||||
|
filter(None, [owner.last_name, owner.first_name, owner.middle_name])
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"name": full_name,
|
||||||
|
"address": owner.address or "",
|
||||||
|
}
|
||||||
|
|
||||||
|
def _contract_context(self, auto, fallback_date):
|
||||||
|
contract_date = auto.contract_date or fallback_date
|
||||||
|
return {
|
||||||
|
"number": auto.registration_number or str(auto.pk),
|
||||||
|
"day": f"{contract_date.day:02d}",
|
||||||
|
"month": UZ_MONTHS.get(contract_date.month, ""),
|
||||||
|
"year": str(contract_date.year),
|
||||||
|
}
|
||||||
|
|
||||||
|
def _empty_analog(self):
|
||||||
|
return {
|
||||||
|
"source": "",
|
||||||
|
"phone": "",
|
||||||
|
"description": "",
|
||||||
|
"year": "",
|
||||||
|
"mileage": "",
|
||||||
|
"price": "",
|
||||||
|
"adjusted_price_1": "",
|
||||||
|
"final_price": "",
|
||||||
|
"weight": "",
|
||||||
|
}
|
||||||
@@ -5,6 +5,17 @@ ENV SCRIPT=$SCRIPT
|
|||||||
|
|
||||||
WORKDIR /code
|
WORKDIR /code
|
||||||
|
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
glib \
|
||||||
|
gdk-pixbuf \
|
||||||
|
pango \
|
||||||
|
cairo \
|
||||||
|
libffi \
|
||||||
|
shared-mime-info \
|
||||||
|
fontconfig \
|
||||||
|
ttf-dejavu \
|
||||||
|
ttf-liberation
|
||||||
|
|
||||||
COPY requirements.txt /code/requirements.txt
|
COPY requirements.txt /code/requirements.txt
|
||||||
|
|
||||||
RUN uv pip install -r requirements.txt
|
RUN uv pip install -r requirements.txt
|
||||||
@@ -16,5 +27,3 @@ COPY ./resources/scripts/$SCRIPT /code/$SCRIPT
|
|||||||
RUN chmod +x /code/resources/scripts/$SCRIPT
|
RUN chmod +x /code/resources/scripts/$SCRIPT
|
||||||
|
|
||||||
CMD sh /code/resources/scripts/$SCRIPT
|
CMD sh /code/resources/scripts/$SCRIPT
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
184
requirements.txt
184
requirements.txt
@@ -1,46 +1,146 @@
|
|||||||
backports.tarfile==1.2.0
|
aiohappyeyeballs
|
||||||
celery==5.4.0
|
aiohttp
|
||||||
django-cors-headers==4.6.0
|
aiosignal
|
||||||
django-environ==0.11.2
|
amqp
|
||||||
django-extensions==3.2.3
|
annotated-doc
|
||||||
django-filter==24.3
|
annotated-types
|
||||||
django-redis==5.4.0
|
arrow
|
||||||
django-unfold==0.65.0
|
asgiref
|
||||||
djangorestframework-simplejwt==5.3.1
|
astor
|
||||||
drf-spectacular==0.28.0
|
attrs
|
||||||
importlib-metadata==8.5.0
|
backports.tarfile
|
||||||
importlib-resources==6.4.5
|
|
||||||
inflect==7.3.1
|
|
||||||
jaraco.collections==5.1.0
|
|
||||||
packaging==24.2
|
|
||||||
pip-chill==1.0.3
|
|
||||||
platformdirs==4.3.6
|
|
||||||
psycopg2-binary==2.9.10
|
|
||||||
tomli==2.2.1
|
|
||||||
uvicorn==0.32.1
|
|
||||||
jst-django-core~=1.2.2
|
|
||||||
rich
|
|
||||||
pydantic
|
|
||||||
bcrypt
|
bcrypt
|
||||||
pytest-django
|
billiard
|
||||||
requests
|
binaryornot
|
||||||
model_bakery
|
black
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
django-modeltranslation~=0.19.11
|
|
||||||
django-ckeditor-5==0.2.15
|
|
||||||
|
|
||||||
django-rosetta==0.10.1
|
|
||||||
django-cacheops~=7.1
|
|
||||||
|
|
||||||
|
|
||||||
# !NOTE: on-server
|
|
||||||
# gunicorn
|
|
||||||
|
|
||||||
|
|
||||||
django-storages
|
|
||||||
boto3
|
boto3
|
||||||
|
botocore
|
||||||
|
brotli
|
||||||
|
celery
|
||||||
|
certifi
|
||||||
|
cffi
|
||||||
|
chardet
|
||||||
|
charset-normalizer
|
||||||
|
click
|
||||||
|
click-didyoumean
|
||||||
|
click-plugins
|
||||||
|
click-repl
|
||||||
|
colorlog
|
||||||
|
cookiecutter
|
||||||
|
cssselect2
|
||||||
|
Django
|
||||||
|
django-cacheops
|
||||||
|
django-ckeditor-5
|
||||||
|
django-cors-headers
|
||||||
|
django-environ
|
||||||
|
django-extensions
|
||||||
|
django-filter
|
||||||
|
django-modeltranslation
|
||||||
|
django-redis
|
||||||
|
django-rosetta
|
||||||
|
django-storages
|
||||||
|
django-unfold
|
||||||
|
djangorestframework
|
||||||
|
djangorestframework-simplejwt
|
||||||
|
drf-spectacular
|
||||||
|
flake8
|
||||||
|
fonttools
|
||||||
|
frozenlist
|
||||||
|
funcy
|
||||||
|
g4f
|
||||||
|
grpcio
|
||||||
|
grpcio-tools
|
||||||
|
h11
|
||||||
|
idna
|
||||||
|
importlib_metadata
|
||||||
|
importlib_resources
|
||||||
|
inflect
|
||||||
|
inflection
|
||||||
|
iniconfig
|
||||||
|
isort
|
||||||
|
jaraco.collections
|
||||||
|
jaraco.context
|
||||||
|
jaraco.functools
|
||||||
|
jaraco.text
|
||||||
|
Jinja2
|
||||||
|
jmespath
|
||||||
|
jsonschema
|
||||||
|
jsonschema-specifications
|
||||||
|
jst-aicommit
|
||||||
|
jst-django
|
||||||
|
jst-django-core
|
||||||
|
kombu
|
||||||
|
markdown-it-py
|
||||||
|
MarkupSafe
|
||||||
|
mccabe
|
||||||
|
mdurl
|
||||||
|
model-bakery
|
||||||
|
more-itertools
|
||||||
|
multidict
|
||||||
|
mypy-extensions
|
||||||
|
nest-asyncio
|
||||||
|
packaging
|
||||||
|
pathspec
|
||||||
|
pillow
|
||||||
|
pip-chill
|
||||||
|
platformdirs
|
||||||
|
pluggy
|
||||||
|
polib
|
||||||
|
prompt-toolkit
|
||||||
|
propcache
|
||||||
|
protobuf
|
||||||
|
psycopg2-binary
|
||||||
|
pycodestyle
|
||||||
|
pycparser
|
||||||
|
pycryptodome
|
||||||
|
pydantic
|
||||||
|
pydantic_core
|
||||||
|
pydyf
|
||||||
|
pyflakes
|
||||||
|
Pygments
|
||||||
|
PyJWT
|
||||||
|
pyphen
|
||||||
|
pytest
|
||||||
|
pytest-django
|
||||||
|
python-dateutil
|
||||||
|
python-slugify
|
||||||
|
PyYAML
|
||||||
|
questionary
|
||||||
|
redis
|
||||||
|
referencing
|
||||||
|
reportlab
|
||||||
|
requests
|
||||||
|
rich
|
||||||
|
rpds-py
|
||||||
|
s3transfer
|
||||||
|
setuptools
|
||||||
|
shellingham
|
||||||
|
six
|
||||||
|
sqlparse
|
||||||
|
tenacity
|
||||||
|
text-unidecode
|
||||||
|
tinycss2
|
||||||
|
tinyhtml5
|
||||||
|
tomli
|
||||||
|
tqdm
|
||||||
|
typeguard
|
||||||
|
typer
|
||||||
|
typer-slim
|
||||||
|
types-python-dateutil
|
||||||
|
typing-inspection
|
||||||
|
typing_extensions
|
||||||
|
tzdata
|
||||||
|
uritemplate
|
||||||
|
urllib3
|
||||||
|
uvicorn
|
||||||
|
vine
|
||||||
|
wcwidth
|
||||||
|
weasyprint
|
||||||
|
webencodings
|
||||||
|
yarl
|
||||||
|
zipfile36
|
||||||
|
zipp
|
||||||
|
zopfli
|
||||||
|
|
||||||
|
|
||||||
# !NOTE: on-websocket
|
# !NOTE: on-websocket
|
||||||
@@ -51,4 +151,4 @@ grpcio>=1.62.0
|
|||||||
grpcio-tools>=1.62.0
|
grpcio-tools>=1.62.0
|
||||||
protobuf>=4.25.0
|
protobuf>=4.25.0
|
||||||
|
|
||||||
reportlab
|
reportlab
|
||||||
|
|||||||
2417
resources/templates/documents/contract.html
Normal file
2417
resources/templates/documents/contract.html
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user