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 — 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": "", }