""" PDF generatsiya view — JSON yuborasiz, PDF olasiz. Yuborilmagan maydonlar uchun bo'sh string ('') qaytariladi — shunda templateda {{ ... }} qoldiqlari ko'rinmaydi. """ import json from django.http import HttpResponse, JsonResponse from django.template.loader import render_to_string from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods from weasyprint import HTML from rest_framework import permissions, views # Kompaniya defaultlari COMPANY_DEFAULTS = { 'company_name': 'SIFAT BAHOLASH', 'membership_number': '122', 'membership_date': '01.06.2023', 'phone_1': '(71) 278-85-85', 'phone_2': '(91) 585-77-77', 'phone_3': '(90) 535-99-99', 'email': 'sifat.baholash@gmail.com', 'bank_account': '2020 8 000 405 309 735 001', 'bank_mfo': '00440', 'inn': '307 930 412', 'director_name': 'Тураев Т.Р.', 'appraiser_name': 'Тураев Т.Р.', 'appraiser_certificate': '0988', 'appraiser_certificate_date': '17.11.2021', 'insurance_policy': '19-01-25/0000368-2025', 'insurance_start': '30.05.2025', 'insurance_end': '29.05.2026', 'market_name': 'Сергели автомобил бозори', 'city': 'Ташкент', 'year': '2026', 'total_pages': 21, 'valuation_purpose': 'для обеспечения залогом', 'valuation_purpose_full': 'определение рыночной стоимости для обеспечения залогом', 'adj_bargaining': '-9,00%', 'adj_tech': '2,00%', 'adj_equipment': '3,00%', 'cost_weight': '20', 'comp_weight': '80', } # Template ichida ishlatiladigan barcha maydonlar ALL_FIELDS = [ 'report_number', 'report_date', 'valuation_date', 'customer_name', 'customer_short_name', 'customer_passport', 'owner_name', 'owner_address', 'vehicle_brand', 'vehicle_model', 'vehicle_plate', 'vehicle_type', 'vehicle_year', 'vehicle_mileage', 'vehicle_engine_number', 'vehicle_body_number', 'vehicle_chassis_number', 'vehicle_color', 'vehicle_tech_passport', 'spec_type', 'spec_seats', 'spec_length', 'spec_width', 'spec_height', 'spec_wheelbase', 'spec_clearance', 'engine_type', 'engine_volume', 'engine_cylinders', 'engine_power', 'engine_torque', 'engine_max_speed', 'engine_acceleration', 'transmission', 'fuel_tank', 'trunk', 'curb_weight', 'gross_weight', 'brakes_front', 'brakes_rear', 'tires', 'cost_c0', 'cost_tf', 'cost_lf', 'cost_type_name', 'cost_kt', 'cost_kl', 'cost_omega', 'cost_wear_andrianov', 'cost_wear_expert', 'cost_wear_avg', 'cost_func_wear', 'cost_total_wear', 'cost_capital', 'cost_approach_value', 'cost_approach_value_words', 'analog_1_source', 'analog_1_year', 'analog_1_mileage', 'analog_1_price', 'analog_1_adj_1', 'analog_1_adj_2', 'analog_1_adj_3', 'analog_1_weight', 'analog_2_source', 'analog_2_year', 'analog_2_mileage', 'analog_2_price', 'analog_2_adj_1', 'analog_2_adj_2', 'analog_2_adj_3', 'analog_2_weight', 'analog_3_source', 'analog_3_year', 'analog_3_mileage', 'analog_3_price', 'analog_3_adj_1', 'analog_3_adj_2', 'analog_3_adj_3', 'analog_3_weight', 'comp_total_usd', 'comp_total_sum', 'comp_total_words', 'cost_specific', 'comp_specific', 'weighted_value', 'market_value_number', 'market_value_words', 'rate_rur', 'rate_usd', 'rate_eur', 'exploitation_period', 'retail_price', ] def _img_or_placeholder(image_url, placeholder_text, alt='', height='200mm'): """Rasm URL bo'lsa , bo'lmasa placeholder qaytaradi""" if image_url: return f'{alt}' return ( f'
' f'{placeholder_text}' f'
' ) def build_context(data: dict) -> dict: """Templatedagi barcha o'zgaruvchilar uchun qiymat tayyorlaydi""" # 1) Barcha maydonlarni bo'sh string bilan to'ldirish context = {field: '' for field in ALL_FIELDS} # 2) Kompaniya defaultlari context.update(COMPANY_DEFAULTS) # 3) Foydalanuvchi yuborgan ma'lumotlarni ustiga yozish context.update(data) # 4) Rasmlar uchun tayyor HTML context['cover_photo_html'] = _img_or_placeholder( data.get('vehicle_photo'), '[ Фото автомобиля ]', alt='Vehicle', height='50mm', ) context['customer_passport_html'] = _img_or_placeholder( data.get('customer_passport_image'), '[ Изображение паспорта заказчика ]', alt='Паспорт заказчика', ) context['tech_passport_html'] = _img_or_placeholder( data.get('tech_passport_image'), '[ Изображение технического паспорта ]', alt='Технический паспорт', ) context['price_list_html'] = _img_or_placeholder( data.get('price_list_image'), '[ Прайс-лист завода-изготовителя ]', alt='Прайс-лист', ) # 5) Loop maydonlar if not context.get('analog_screenshots'): context['analog_screenshots'] = [ {'page_num': 18, 'image': None}, {'page_num': 19, 'image': None}, {'page_num': 20, 'image': None}, ] if not context.get('vehicle_photos'): context['vehicle_photos'] = [] return context class GenerateApiView(views.APIView): def post(self, request): try: html_string = render_to_string('report_template.html', context) pdf_bytes = HTML(string=html_string).write_pdf() except Exception as e: return JsonResponse({'error': f'PDF yaratishda xato: {str(e)}'}, status=500) filename = f'otchet_1.pdf' response = HttpResponse(pdf_bytes, content_type='application/pdf') response['Content-Disposition'] = f'attachment; filename="{filename}"' return response