Files
fonrey/apps/property/migrations/0001_initial.py

659 lines
42 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Generated by Django 4.2.16 on 2026-04-29 09:26
import django.contrib.postgres.indexes
import django.contrib.postgres.search
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
('fonrey_complex', '0002_pg_trgm_and_search_vector'),
('org', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='FollowLog',
fields=[
('id', models.UUIDField(primary_key=True, serialize=False)),
('created_at', models.DateTimeField()),
('log_type', models.CharField(choices=[('written', '手写跟进'), ('modified', '修改跟进'), ('sensitive_op', '敏感操作'), ('sensitive_view', '敏感查看'), ('other', '其他'), ('system', '系统')], max_length=30)),
('purpose', models.CharField(blank=True, default='', max_length=50)),
('content', models.TextField(blank=True, default='')),
('ai_tag', models.CharField(blank=True, choices=[('ai_for_sale', 'AI判断可售'), ('ai_not_for_sale', 'AI判断不可售')], default='', max_length=20)),
('change_detail', models.JSONField(blank=True, null=True)),
('log_tag', models.CharField(blank=True, default='', max_length=50)),
('is_public', models.BooleanField(default=True)),
('operator_snapshot', models.JSONField(blank=True, null=True)),
('is_deletable', models.BooleanField(default=True)),
('deleted_at', models.DateTimeField(blank=True, null=True)),
],
options={
'db_table': 'follow_logs',
'managed': False,
},
),
migrations.CreateModel(
name='PropertyPhoto',
fields=[
('id', models.UUIDField(primary_key=True, serialize=False)),
('created_at', models.DateTimeField()),
('category', models.CharField(choices=[('cover', '封面'), ('entrance', '入户'), ('living_room', '客厅'), ('dining_room', '餐厅'), ('bedroom', '卧室'), ('bathroom', '卫生间'), ('kitchen', '厨房'), ('balcony', '阳台'), ('study', '书房'), ('indoor_other', '室内其他'), ('outdoor', '室外'), ('panorama', '全景')], max_length=20)),
('file_key', models.TextField()),
('thumbnail_key', models.TextField(blank=True, default='')),
('file_name', models.CharField(blank=True, default='', max_length=255)),
('file_size', models.IntegerField(blank=True, null=True)),
('width', models.IntegerField(blank=True, null=True)),
('height', models.IntegerField(blank=True, null=True)),
('is_cover', models.BooleanField(default=False)),
('sort_order', models.SmallIntegerField(default=0)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'db_table': 'property_photos',
'managed': False,
},
),
migrations.CreateModel(
name='Commission',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('commission_type', models.CharField(max_length=50)),
('period_start', models.DateField()),
('period_end', models.DateField(blank=True, null=True)),
('is_open_ended', models.BooleanField(default=False)),
('agent_snapshot', models.JSONField(blank=True, null=True)),
('signing_method', models.CharField(blank=True, default='', max_length=50)),
('owner_type', models.CharField(choices=[('owner', '产权人本人'), ('authorized_third', '授权第三方')], default='owner', max_length=20)),
('owner_name', models.CharField(blank=True, default='', max_length=50)),
('owner_id_type', models.CharField(blank=True, default='', max_length=20)),
('owner_id_number', models.CharField(blank=True, default='', max_length=50)),
('owner_id_number_enc', models.BinaryField(blank=True, null=True)),
('remarks', models.TextField(blank=True, default='')),
('status', models.CharField(choices=[('active', '有效'), ('expired', '过期'), ('cancelled', '取消')], default='active', max_length=20)),
('agent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='commissions_as_agent', to='org.staff')),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_commissions', to='org.staff')),
],
options={
'db_table': 'commissions',
},
),
migrations.CreateModel(
name='Property',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('deleted_at', models.DateTimeField(blank=True, db_index=True, null=True)),
('property_type', models.CharField(choices=[('residential', '住宅'), ('villa', '别墅'), ('commercial_residential', '商住'), ('shop', '商铺'), ('office', '写字楼'), ('other', '其他')], max_length=30)),
('status', models.CharField(choices=[('for_sale', '出售'), ('for_rent', '出租'), ('for_sale_rent', '租售'), ('suspended', '暂缓'), ('sold_elsewhere', '他售'), ('rented_elsewhere', '他租'), ('sold', '成交'), ('unlisted', '未挂牌')], default='for_sale', max_length=20)),
('attribute', models.CharField(choices=[('public', '公盘'), ('private', '私盘'), ('special', '特盘'), ('sealed', '封盘')], default='public', max_length=10)),
('private_reason', models.TextField(blank=True, default='')),
('block_no', models.CharField(blank=True, default='', max_length=30)),
('unit_no', models.CharField(blank=True, default='', max_length=30)),
('room_no', models.CharField(blank=True, default='', max_length=30)),
('floor', models.SmallIntegerField()),
('total_floors', models.SmallIntegerField()),
('bedroom_count', models.SmallIntegerField(default=0)),
('living_room_count', models.SmallIntegerField(default=0)),
('bathroom_count', models.SmallIntegerField(default=0)),
('kitchen_count', models.SmallIntegerField(default=0)),
('balcony_count', models.SmallIntegerField(default=0)),
('area', models.DecimalField(decimal_places=2, max_digits=8)),
('inner_area', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True)),
('sale_price', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)),
('sale_bottom_price', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)),
('sale_record_price', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)),
('rent_price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
('orientation', models.CharField(blank=True, choices=[('east', ''), ('south', ''), ('west', '西'), ('north', ''), ('southeast', '东南'), ('northeast', '东北'), ('east_west', '东西'), ('south_north', '南北'), ('northwest', '西北'), ('southwest', '西南')], default='', max_length=15)),
('decoration', models.CharField(blank=True, choices=[('rough', '毛坯'), ('plain', '清水'), ('simple', '简装'), ('medium', '中装'), ('fine', '精装'), ('luxury', '豪装')], default='', max_length=10)),
('has_elevator', models.BooleanField(blank=True, null=True)),
('built_year', models.SmallIntegerField(blank=True, null=True)),
('usage_type', models.CharField(blank=True, default='', max_length=30)),
('usage_subtype', models.CharField(blank=True, default='', max_length=30)),
('shop_frontage', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True)),
('shop_depth', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True)),
('shop_height', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True)),
('shop_location', models.CharField(blank=True, choices=[('street', '临街商铺'), ('mall', '商场'), ('residential', '住宅底商'), ('ground_floor', '底层'), ('complex', '综合体')], default='', max_length=20)),
('house_status', models.CharField(blank=True, choices=[('owner_occupied', '业主自住'), ('vacant', '空置'), ('tenant_occupied', '租客在住'), ('unknown', '未知')], default='', max_length=20)),
('viewing_time', models.CharField(blank=True, choices=[('anytime', '随时看房'), ('by_appointment', '预约看房'), ('inconvenient', '不便看房')], default='', max_length=20)),
('grade', models.CharField(blank=True, choices=[('a', 'A急迫'), ('b', 'B较强'), ('c', 'C一般'), ('d', 'D较弱')], default='', max_length=2)),
('ownership_years', models.CharField(blank=True, default='', max_length=30)),
('ownership_years_detail', models.CharField(blank=True, default='', max_length=20)),
('ownership_nature', models.CharField(blank=True, choices=[('commercial', '商品房'), ('reform_housing', '房改房'), ('collective', '集资房'), ('economic', '经济适用房')], default='', max_length=20)),
('is_only_house', models.BooleanField(blank=True, null=True)),
('payment_method', models.CharField(blank=True, choices=[('full', '全款'), ('mortgage', '按揭'), ('installment', '分期'), ('advance', '垫资')], default='', max_length=15)),
('tax_included', models.CharField(blank=True, choices=[('each_party', '各付'), ('net', '净到手'), ('inclusive', '包税')], default='', max_length=15)),
('has_mortgage', models.BooleanField(blank=True, null=True)),
('has_loan', models.BooleanField(blank=True, null=True)),
('has_seal', models.BooleanField(blank=True, null=True)),
('has_restriction', models.BooleanField(blank=True, null=True)),
('original_price', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)),
('sale_reason', models.TextField(blank=True, default='')),
('remarks', models.TextField(blank=True, default='')),
('source', models.CharField(blank=True, default='', max_length=50)),
('completeness_score', models.SmallIntegerField(default=0)),
('listed_at', models.DateTimeField(blank=True, null=True)),
('last_followed_at', models.DateTimeField(blank=True, null=True)),
('search_vector', django.contrib.postgres.search.SearchVectorField(blank=True, null=True)),
('version', models.IntegerField(default=1)),
('building', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='properties', to='fonrey_complex.building')),
('buyer_agent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='buying_properties', to='org.staff')),
('complex', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='properties', to='fonrey_complex.complex')),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_properties', to='org.staff')),
('first_recorder', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='first_recorded_properties', to='org.staff')),
('number_holder', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='held_properties', to='org.staff')),
('seller_agent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='selling_properties', to='org.staff')),
('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='updated_properties', to='org.staff')),
],
options={
'db_table': 'properties',
},
),
migrations.CreateModel(
name='PropertyTag',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('name', models.CharField(max_length=50)),
('color', models.CharField(blank=True, default='', max_length=7)),
('is_system', models.BooleanField(default=False)),
('sort_order', models.IntegerField(default=0)),
('is_active', models.BooleanField(default=True)),
],
options={
'db_table': 'property_tags',
},
),
migrations.CreateModel(
name='PropertyProtection',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('is_protected', models.BooleanField(default=False)),
('reason', models.TextField(blank=True, default='')),
('start_at', models.DateTimeField(blank=True, null=True)),
('end_at', models.DateTimeField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('property', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='protection', to='fonrey_property.property')),
('set_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='org.staff')),
],
options={
'db_table': 'property_protections',
},
),
migrations.CreateModel(
name='PropertyMarketing',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('marketing_title', models.CharField(blank=True, default='', max_length=30)),
('core_selling_points', models.TextField(blank=True, default='')),
('owner_attitude', models.TextField(blank=True, default='')),
('layout_description', models.TextField(blank=True, default='')),
('complex_description', models.TextField(blank=True, default='')),
('ai_generated_points', models.BooleanField(default=False)),
('ai_generated_attitude', models.BooleanField(default=False)),
('updated_at', models.DateTimeField(auto_now=True)),
('property', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='marketing', to='fonrey_property.property')),
('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='org.staff')),
],
options={
'db_table': 'property_marketing',
},
),
migrations.CreateModel(
name='PropertyKey',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('key_type', models.CharField(choices=[('mechanical', '机械钥匙'), ('password', '密码钥匙')], max_length=20)),
('holder_snapshot', models.JSONField(blank=True, null=True)),
('is_other_agency', models.BooleanField(default=False)),
('other_agency_info', models.CharField(blank=True, default='', max_length=30)),
('remarks', models.TextField(blank=True, default='')),
('is_active', models.BooleanField(default=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_property_keys', to='org.staff')),
('holder', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='held_keys', to='org.staff')),
('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='keys', to='fonrey_property.property')),
('storage_unit', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stored_keys', to='org.orgunit')),
],
options={
'db_table': 'property_keys',
},
),
migrations.CreateModel(
name='PropertyFavorite',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='favorited_by', to='fonrey_property.property')),
('staff', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='favorite_properties', to='org.staff')),
],
options={
'db_table': 'property_favorites',
},
),
migrations.CreateModel(
name='PropertyContact',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('deleted_at', models.DateTimeField(blank=True, db_index=True, null=True)),
('name', models.CharField(max_length=50)),
('gender', models.CharField(choices=[('male', '先生'), ('female', '女士')], default='male', max_length=10)),
('identity', models.CharField(choices=[('owner', '业主'), ('contact', '联系人'), ('subletter', '转租人'), ('tenant', '租客'), ('agent', '代理人'), ('corporate', '企业法人')], default='contact', max_length=20)),
('phone_enc', models.BinaryField()),
('phone_hash', models.CharField(max_length=64)),
('phone2_enc', models.BinaryField(blank=True, null=True)),
('phone2_hash', models.CharField(blank=True, default='', max_length=64)),
('wechat', models.CharField(blank=True, default='', max_length=100)),
('qq', models.CharField(blank=True, default='', max_length=20)),
('remarks', models.TextField(blank=True, default='')),
('is_number_holder', models.BooleanField(default=False)),
('number_holder_approved_at', models.DateTimeField(blank=True, null=True)),
('sort_order', models.IntegerField(default=0)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_property_contacts', to='org.staff')),
('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contacts', to='fonrey_property.property')),
('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='updated_property_contacts', to='org.staff')),
],
options={
'db_table': 'property_contacts',
},
),
migrations.CreateModel(
name='PropertyCompleteness',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('score_core_info', models.SmallIntegerField(default=0)),
('score_attachment', models.SmallIntegerField(default=0)),
('score_survey', models.SmallIntegerField(default=0)),
('score_vr', models.SmallIntegerField(default=0)),
('score_key', models.SmallIntegerField(default=0)),
('score_commission', models.SmallIntegerField(default=0)),
('score_verification', models.SmallIntegerField(default=0)),
('score_follow_up', models.SmallIntegerField(default=0)),
('score_viewing', models.SmallIntegerField(default=0)),
('score_other', models.SmallIntegerField(default=0)),
('total_score', models.SmallIntegerField(default=0)),
('calculated_at', models.DateTimeField(auto_now=True)),
('property', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='completeness', to='fonrey_property.property')),
],
options={
'db_table': 'property_completeness',
},
),
migrations.CreateModel(
name='PropertyCertificate',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('owner_name', models.CharField(blank=True, default='', max_length=100)),
('owner_id_number', models.CharField(blank=True, default='', max_length=50)),
('owner_cert_type', models.CharField(blank=True, default='', max_length=20)),
('property_location', models.CharField(blank=True, default='', max_length=500)),
('cert_status', models.CharField(blank=True, default='', max_length=30)),
('cert_no', models.CharField(blank=True, default='', max_length=100)),
('first_registered_at', models.DateField(blank=True, null=True)),
('ownership_nature', models.CharField(blank=True, default='', max_length=30)),
('land_nature', models.CharField(blank=True, default='', max_length=30)),
('updated_at', models.DateTimeField(auto_now=True)),
('property', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='certificate', to='fonrey_property.property')),
('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='org.staff')),
],
options={
'db_table': 'property_certificates',
},
),
migrations.CreateModel(
name='PropertyAttachment',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('category', models.CharField(choices=[('id_card', '身份证件'), ('property_cert', '产权证明'), ('commission_letter', '委托书'), ('other', '其他')], default='other', max_length=20)),
('file_key', models.TextField()),
('file_name', models.CharField(max_length=255)),
('file_size', models.IntegerField()),
('file_type', models.CharField(blank=True, default='', max_length=50)),
('sort_order', models.SmallIntegerField(default=0)),
('created_at', models.DateTimeField(auto_now_add=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='org.staff')),
('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='fonrey_property.property')),
],
options={
'db_table': 'property_attachments',
},
),
migrations.CreateModel(
name='PriceChange',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('old_sale_price', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)),
('new_sale_price', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)),
('old_bottom_price', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)),
('new_bottom_price', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)),
('old_record_price', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)),
('new_record_price', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)),
('old_rent_price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
('new_rent_price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
('change_reason', models.TextField()),
('changed_at', models.DateTimeField(auto_now_add=True)),
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='org.staff')),
('property', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='price_changes', to='fonrey_property.property')),
],
options={
'db_table': 'price_changes',
},
),
migrations.CreateModel(
name='NumberHolderApproval',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('status', models.CharField(choices=[('pending', '待审批'), ('approved', '已通过'), ('rejected', '已驳回')], default='pending', max_length=20)),
('remarks', models.TextField(blank=True, default='')),
('created_at', models.DateTimeField(auto_now_add=True)),
('decided_at', models.DateTimeField(blank=True, null=True)),
('applicant', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='nh_applications', to='org.staff')),
('approver', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='nh_approvals', to='org.staff')),
('contact', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='number_holder_approvals', to='fonrey_property.propertycontact')),
('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='number_holder_approvals', to='fonrey_property.property')),
],
options={
'db_table': 'number_holder_approvals',
},
),
migrations.CreateModel(
name='ListingHistory',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('listing_type', models.CharField(choices=[('for_sale', '出售挂牌'), ('for_rent', '出租挂牌')], max_length=20)),
('status', models.CharField(choices=[('active', '生效中'), ('ended', '已结束')], default='active', max_length=10)),
('sale_price', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)),
('rent_price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
('sale_unit_price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
('ownership_years', models.CharField(blank=True, default='', max_length=30)),
('is_only_house', models.BooleanField(blank=True, null=True)),
('tax_included', models.CharField(blank=True, default='', max_length=15)),
('sale_reason', models.TextField(blank=True, default='')),
('seller_agent_snapshot', models.JSONField(blank=True, null=True)),
('started_at', models.DateTimeField()),
('ended_at', models.DateTimeField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('property', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='listing_histories', to='fonrey_property.property')),
('seller_agent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='org.staff')),
],
options={
'db_table': 'listing_histories',
},
),
migrations.CreateModel(
name='KeyAttachment',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('file_key', models.TextField()),
('file_name', models.CharField(max_length=255)),
('created_at', models.DateTimeField(auto_now_add=True)),
('key', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='fonrey_property.propertykey')),
],
options={
'db_table': 'key_attachments',
},
),
migrations.CreateModel(
name='FollowLogRecording',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('follow_log_id', models.UUIDField()),
('file_key', models.TextField()),
('duration_seconds', models.IntegerField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
],
options={
'db_table': 'follow_log_recordings',
'indexes': [models.Index(fields=['follow_log_id'], name='idx_flr_log')],
},
),
migrations.CreateModel(
name='FollowLogAttachment',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('follow_log_id', models.UUIDField()),
('file_key', models.TextField()),
('file_name', models.CharField(max_length=255)),
('file_size', models.IntegerField()),
('file_type', models.CharField(blank=True, choices=[('bmp', 'BMP'), ('jpg', 'JPG'), ('png', 'PNG'), ('svg', 'SVG'), ('gif', 'GIF')], default='', max_length=10)),
('sort_order', models.SmallIntegerField(default=0)),
('created_at', models.DateTimeField(auto_now_add=True)),
],
options={
'db_table': 'follow_log_attachments',
'indexes': [models.Index(fields=['follow_log_id'], name='idx_fla_log')],
},
),
migrations.CreateModel(
name='FieldSurvey',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('status', models.CharField(choices=[('draft', '草稿'), ('submitted', '已提交')], default='draft', max_length=10)),
('gps_latitude', models.DecimalField(blank=True, decimal_places=7, max_digits=10, null=True)),
('gps_longitude', models.DecimalField(blank=True, decimal_places=7, max_digits=10, null=True)),
('gps_accuracy', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True)),
('description', models.TextField(blank=True, default='')),
('submitted_at', models.DateTimeField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='org.staff')),
('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='field_surveys', to='fonrey_property.property')),
],
options={
'db_table': 'field_surveys',
},
),
migrations.CreateModel(
name='CommissionAttachment',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('category', models.CharField(choices=[('id_card', '身份证件'), ('property_cert', '产权证明'), ('commission_letter', '委托书'), ('other', '其他')], max_length=20)),
('file_key', models.TextField()),
('file_name', models.CharField(max_length=255)),
('file_size', models.IntegerField(blank=True, null=True)),
('sort_order', models.SmallIntegerField(default=0)),
('created_at', models.DateTimeField(auto_now_add=True)),
('commission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='fonrey_property.commission')),
],
options={
'db_table': 'commission_attachments',
},
),
migrations.AddField(
model_name='commission',
name='property',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='commissions', to='fonrey_property.property'),
),
migrations.AddField(
model_name='commission',
name='property_owner_contact',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='commissions', to='fonrey_property.propertycontact'),
),
migrations.CreateModel(
name='SurveyPhoto',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('category', models.CharField(choices=[('layout', '户型图'), ('living_room', '客厅'), ('dining_room', '餐厅'), ('bedroom', '卧室'), ('bathroom', '卫生间'), ('kitchen', '厨房'), ('entrance', '入户'), ('balcony', '阳台'), ('study', '书房'), ('indoor_other', '室内其他'), ('outdoor', '室外')], max_length=20)),
('file_key', models.TextField()),
('thumbnail_key', models.TextField(blank=True, default='')),
('file_size', models.IntegerField(blank=True, null=True)),
('width', models.IntegerField(blank=True, null=True)),
('height', models.IntegerField(blank=True, null=True)),
('sort_order', models.SmallIntegerField(default=0)),
('is_vr_screenshot', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('survey', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='photos', to='fonrey_property.fieldsurvey')),
],
options={
'db_table': 'survey_photos',
'indexes': [models.Index(fields=['survey'], name='idx_sp_survey'), models.Index(fields=['survey', 'category'], name='idx_sp_category')],
},
),
migrations.CreateModel(
name='PropertyTagRelation',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tag_relations', to='fonrey_property.property')),
('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='property_relations', to='fonrey_property.propertytag')),
],
options={
'db_table': 'property_tag_relations',
'indexes': [models.Index(fields=['property'], name='idx_ptr_property'), models.Index(fields=['tag'], name='idx_ptr_tag')],
},
),
migrations.AddConstraint(
model_name='propertytagrelation',
constraint=models.UniqueConstraint(fields=('property', 'tag'), name='uq_ptr_property_tag'),
),
migrations.AddIndex(
model_name='propertykey',
index=models.Index(fields=['property'], name='idx_pk_property'),
),
migrations.AddIndex(
model_name='propertyfavorite',
index=models.Index(fields=['staff'], name='idx_pfav_staff'),
),
migrations.AddConstraint(
model_name='propertyfavorite',
constraint=models.UniqueConstraint(fields=('staff', 'property'), name='uq_pfav_staff_property'),
),
migrations.AddIndex(
model_name='propertycontact',
index=models.Index(fields=['property'], name='idx_pc_property'),
),
migrations.AddIndex(
model_name='propertycontact',
index=models.Index(fields=['phone_hash'], name='idx_pc_phone_hash'),
),
migrations.AddIndex(
model_name='propertycontact',
index=models.Index(fields=['phone2_hash'], name='idx_pc_phone2_hash'),
),
migrations.AddIndex(
model_name='propertyattachment',
index=models.Index(fields=['property'], name='idx_pa_property'),
),
migrations.AddIndex(
model_name='propertyattachment',
index=models.Index(fields=['property', 'category'], name='idx_pa_category'),
),
migrations.AddIndex(
model_name='property',
index=django.contrib.postgres.indexes.GinIndex(fields=['search_vector'], name='idx_properties_search'),
),
migrations.AddIndex(
model_name='property',
index=models.Index(fields=['complex'], name='idx_properties_complex'),
),
migrations.AddIndex(
model_name='property',
index=models.Index(fields=['status'], name='idx_properties_status'),
),
migrations.AddIndex(
model_name='property',
index=models.Index(fields=['sale_price'], name='idx_properties_sale_price'),
),
migrations.AddIndex(
model_name='property',
index=models.Index(fields=['area'], name='idx_properties_area'),
),
migrations.AddIndex(
model_name='property',
index=models.Index(fields=['listed_at'], name='idx_properties_listed_at'),
),
migrations.AddIndex(
model_name='property',
index=models.Index(fields=['last_followed_at'], name='idx_properties_last_followed'),
),
migrations.AddIndex(
model_name='property',
index=models.Index(fields=['bedroom_count'], name='idx_properties_bedroom'),
),
migrations.AddIndex(
model_name='property',
index=models.Index(fields=['grade'], name='idx_properties_grade'),
),
migrations.AddIndex(
model_name='property',
index=models.Index(fields=['completeness_score'], name='idx_properties_completeness'),
),
migrations.AddIndex(
model_name='property',
index=models.Index(fields=['seller_agent'], name='idx_properties_seller_agent'),
),
migrations.AddIndex(
model_name='property',
index=models.Index(fields=['number_holder'], name='idx_properties_number_holder'),
),
migrations.AddIndex(
model_name='property',
index=models.Index(fields=['status', 'attribute', 'complex', 'sale_price'], name='idx_properties_list_composite'),
),
migrations.AddIndex(
model_name='property',
index=models.Index(fields=['seller_agent', 'status', 'listed_at'], name='idx_properties_my_properties'),
),
migrations.AddConstraint(
model_name='property',
constraint=models.CheckConstraint(check=models.Q(('floor__gt', 0), ('floor__lte', models.F('total_floors'))), name='chk_property_floor'),
),
migrations.AddIndex(
model_name='pricechange',
index=models.Index(fields=['property'], name='idx_pchg_property'),
),
migrations.AddIndex(
model_name='pricechange',
index=models.Index(fields=['property', '-changed_at'], name='idx_pchg_time'),
),
migrations.AddIndex(
model_name='numberholderapproval',
index=models.Index(fields=['status'], name='idx_nha_status'),
),
migrations.AddIndex(
model_name='numberholderapproval',
index=models.Index(fields=['property'], name='idx_nha_property'),
),
migrations.AddIndex(
model_name='listinghistory',
index=models.Index(fields=['property'], name='idx_lh_property'),
),
migrations.AddIndex(
model_name='listinghistory',
index=models.Index(fields=['property', 'status'], name='idx_lh_active'),
),
migrations.AddIndex(
model_name='keyattachment',
index=models.Index(fields=['key'], name='idx_ka_key'),
),
migrations.AddIndex(
model_name='fieldsurvey',
index=models.Index(fields=['property'], name='idx_fs_property'),
),
migrations.AddIndex(
model_name='fieldsurvey',
index=models.Index(fields=['property', 'status'], name='idx_fs_submitted'),
),
migrations.AddIndex(
model_name='commissionattachment',
index=models.Index(fields=['commission'], name='idx_ca_commission'),
),
migrations.AddIndex(
model_name='commission',
index=models.Index(fields=['property'], name='idx_commissions_property'),
),
migrations.AddIndex(
model_name='commission',
index=models.Index(fields=['property', 'status'], name='idx_commissions_active'),
),
]