15 KiB
Executable file
تحلیل انتقال اشخاص از hesabixOld به hesabixpy
خلاصه اجرایی
این سند تحلیل نحوه انتقال اشخاص (persons/contacts) از دیتابیس قدیمی (hesabixOld) به دیتابیس جدید (hesabixpy) را ارائه میدهد.
وضعیت فعلی
دیتابیس قدیمی (hesabixOld)
جدول person
- تعداد اشخاص: 46,646
- تعداد کسب و کارهای دارای شخص: 2,597
- ساختار:
id: شناسه یکتاbid_id: شناسه کسب و کار (ارجاع بهbusiness.id)code: کد یکتا در کسب و کار (bigint)nikename: نام مستعار (varchar(255))name: نام (varchar(255))tel: تلفن (varchar(12))mobile: موبایل (varchar(12))mobile2: موبایل دوم (varchar(15))address: آدرس (varchar(255))des: توضیحات (varchar(255))company: نام شرکت (varchar(255))shenasemeli: شناسه ملی (varchar(255))codeeghtesadi: شناسه اقتصادی (varchar(255))sabt: شماره ثبت (varchar(255))keshvar: کشور (varchar(255))ostan: استان (varchar(255))shahr: شهر (varchar(255))postalcode: کد پستی (varchar(255))email: ایمیل (varchar(255))website: وبسایت (varchar(255))fax: فکس (varchar(255))birthday: تاریخ تولد (varchar(255))payment_id: شناسه پرداخت (varchar(255))prelabel_id: شناسه پیشبرچسب (int)tags: تگها (longtext)- فیلدهای خاص:
plug_noghre_morsa,plug_noghre_hakak,plug_noghre_tarash,employe,plug_noghre_ghalam
آمار:
- اشخاص با شرکت: 4,250 (9.1%)
- اشخاص با شناسه ملی: 1,072 (2.3%)
- اشخاص قابل انتقال: 44,754 (96.0%)
- کسب و کارهای قابل انتقال: 2,356
جدول person_type
- ساختار:
id: شناسه یکتاlabel: برچسب (varchar(255)) - مثل "مشتری", "تامینکننده"code: کد (varchar(255)) - مثل "customer", "supplier"
انواع موجود:
- مشتری (customer)
- بازاریاب (marketer)
- کارمند (emplyee)
- تامینکننده (supplier)
- همکار (colleague)
- فروشنده (salesman)
جدول person_person_type
- ساختار:
person_id: شناسه شخص (ارجاع بهperson.id)person_type_id: شناسه نوع شخص (ارجاع بهperson_type.id)
آمار:
- کل رکوردها: 21,356
- اشخاص منحصر به فرد با نوع: 19,751
- اشخاص با چندین نوع: 916
توزیع انواع:
- مشتری: 14,314 (67.0%)
- تامینکننده: 2,705 (12.7%)
- همکار: 2,077 (9.7%)
- کارمند: 1,097 (5.1%)
- فروشنده: 906 (4.2%)
- بازاریاب: 257 (1.2%)
دیتابیس جدید (hesabixpy)
جدول persons
- تعداد اشخاص فعلی: 122
- ساختار:
id: شناسه یکتاbusiness_id: شناسه کسب و کار (ارجاع بهbusinesses.id)code: کد یکتا در هر کسب و کار (integer)alias_name: نام مستعار (varchar(255)) - الزامیfirst_name: نام (varchar(100))last_name: نام خانوادگی (varchar(100))person_types: لیست انواع شخص به صورت JSON (text) - الزامیcompany_name: نام شرکت (varchar(255))payment_id: شناسه پرداخت (varchar(100))national_id: شناسه ملی (varchar(20))registration_number: شماره ثبت (varchar(50))economic_id: شناسه اقتصادی (varchar(50))country: کشور (varchar(100))province: استان (varchar(100))city: شهرستان (varchar(100))address: آدرس (text)postal_code: کد پستی (varchar(20))phone: تلفن (varchar(20))mobile: موبایل (varchar(20))fax: فکس (varchar(20))email: ایمیل (varchar(255))website: وبسایت (varchar(255))share_count: تعداد سهام (integer)commission_sale_percent: درصد پورسانت از فروش (decimal(5,2))commission_sales_return_percent: درصد پورسانت از برگشت از فروش (decimal(5,2))commission_sales_amount: مبلغ فروش مبنا برای پورسانت (decimal(12,2))commission_sales_return_amount: مبلغ برگشت از فروش مبنا برای پورسانت (decimal(12,2))commission_exclude_discounts: عدم محاسبه تخفیف در پورسانت (boolean)commission_exclude_additions_deductions: عدم محاسبه اضافات و کسورات فاکتور در پورسانت (boolean)commission_post_in_invoice_document: ثبت پورسانت در سند حسابداری فاکتور (boolean)credit_limit: سقف اعتبار شخص (decimal(14,2))credit_check_enabled: فعال بودن بررسی اعتبار (boolean)created_at: تاریخ ایجاد (datetime)updated_at: تاریخ بهروزرسانی (datetime)
نکته مهم: person_types به صورت JSON array نگهداری میشود، مثل: ["مشتری", "تامینکننده"]
استراتژی انتقال
تبدیل فیلدها
| فیلد قدیمی | فیلد جدید | تبدیل |
|---|---|---|
id |
- | نگهداری در mapping table (اختیاری) |
bid_id |
business_id |
نگاشت از business_id_mapping |
code |
code |
تبدیل bigint به integer (با بررسی محدوده) |
nikename |
alias_name |
مستقیم (الزامی) |
name |
first_name, last_name |
تقسیم نام به نام و نام خانوادگی |
company |
company_name |
مستقیم |
shenasemeli |
national_id |
مستقیم |
codeeghtesadi |
economic_id |
مستقیم |
sabt |
registration_number |
مستقیم |
keshvar |
country |
مستقیم |
ostan |
province |
مستقیم |
shahr |
city |
مستقیم |
address |
address |
مستقیم |
postalcode |
postal_code |
مستقیم |
tel |
phone |
مستقیم |
mobile |
mobile |
مستقیم |
mobile2 |
- | حذف (یا در توضیحات) |
fax |
fax |
مستقیم |
email |
email |
مستقیم |
website |
website |
مستقیم |
birthday |
- | حذف (یا در توضیحات) |
payment_id |
payment_id |
مستقیم |
des |
- | حذف (یا در توضیحات) |
tags |
- | حذف (یا در جدول جداگانه) |
person_person_type |
person_types |
تبدیل به JSON array |
تبدیل name به first_name و last_name
الگوریتم:
def split_name(name: str | None) -> Tuple[str | None, str | None]:
"""
تبدیل name به first_name و last_name
Args:
name: نام کامل (مثل "علی زیوری" یا "صادق احمدیان")
Returns:
(first_name, last_name)
"""
if not name or not name.strip():
return (None, None)
name = name.strip()
parts = name.split()
if len(parts) == 0:
return (None, None)
elif len(parts) == 1:
return (parts[0], None)
else:
# اولین کلمه = نام، بقیه = نام خانوادگی
first_name = parts[0]
last_name = ' '.join(parts[1:])
return (first_name, last_name)
نکته: اگر name خالی باشد، از nikename استفاده میشود.
تبدیل person_types
چالش:
- در قدیمی: جدول
person_person_typeبا چندین رکورد برای هر شخص - در جدید: فیلد
person_typesبه صورت JSON array
راهحل:
- برای هر شخص، تمام
person_type_idرا ازperson_person_typeبخوان labelهر نوع را ازperson_typeبگیر- تبدیل به JSON array:
["مشتری", "تامینکننده"] - اگر شخصی نوع نداشت، پیشفرض:
["مشتری"]
الگوریتم:
def get_person_types(old_person_id: int, person_type_mapping: Dict[int, str]) -> List[str]:
"""
دریافت انواع شخص از دیتابیس قدیمی
Args:
old_person_id: شناسه شخص قدیمی
person_type_mapping: mapping {person_type_id: label}
Returns:
لیست انواع شخص (مثل ["مشتری", "تامینکننده"])
"""
query = text("""
SELECT person_type_id
FROM hesabixOld.person_person_type
WHERE person_id = :person_id
""")
results = old_db.execute(query, {"person_id": old_person_id}).fetchall()
types = []
for row in results:
type_label = person_type_mapping.get(row.person_type_id)
if type_label:
types.append(type_label)
# اگر نوعی نداشت، پیشفرض مشتری
if not types:
types = ["مشتری"]
return types
def convert_person_types_to_json(types: List[str]) -> str:
"""تبدیل لیست انواع به JSON"""
return json.dumps(types, ensure_ascii=False)
Mapping انواع:
- مشتری (customer) → "مشتری"
- بازاریاب (marketer) → "بازاریاب"
- کارمند (emplyee) → "کارمند"
- تامینکننده (supplier) → "تامینکننده"
- همکار (colleague) → "همکار"
- فروشنده (salesman) → "فروشنده"
تبدیل code
چالش:
- در قدیمی:
codeبه صورتbigint - در جدید:
codeبه صورتinteger(محدود به 2147483647)
راهحل:
def convert_code(code: int | None) -> int | None:
"""تبدیل code از bigint به integer"""
if code is None:
return None
# محدود کردن به محدوده integer
if code > 2147483647:
return None # یا استفاده از code جدید
return int(code)
تبدیل alias_name
الگوریتم:
def get_alias_name(nikename: str | None, name: str | None) -> str:
"""
دریافت alias_name از nikename یا name
Args:
nikename: نام مستعار
name: نام کامل
Returns:
alias_name (الزامی)
"""
if nikename and nikename.strip():
return nikename.strip()
elif name and name.strip():
return name.strip()
else:
# اگر هیچکدام نبود، از "شخص بدون نام" استفاده میکنیم
return "شخص بدون نام"
الگوریتم انتقال
مرحله 1: آمادهسازی
- ایجاد business_id_mapping: از انتقال کسب و کارها
- ایجاد person_type_mapping: mapping بین
person_type.idوperson_type.label
مرحله 2: فیلتر اشخاص برای انتقال
شرایط انتقال:
- شخص باید
bid_idداشته باشد bid_idباید درbusiness_id_mappingوجود داشته باشد- شخص نباید در دیتابیس جدید وجود داشته باشد (بر اساس business_id و code)
SQL برای انتخاب:
SELECT p.*
FROM hesabixOld.person p
INNER JOIN business_id_mapping m ON p.bid_id = m.old_business_id
WHERE NOT EXISTS (
SELECT 1 FROM hesabixpy.persons new
WHERE new.business_id = m.new_business_id
AND new.code = p.code
)
ORDER BY p.bid_id, p.id;
مرحله 3: پردازش هر شخص
مراحل:
- نگاشت
bid_id→business_id - تبدیل
code(bigint به integer) - تبدیل
nikename/name→alias_name - تقسیم
name→first_nameوlast_name - تبدیل
person_person_type→person_types(JSON) - تبدیل سایر فیلدها
- درج در دیتابیس جدید
مرحله 4: مدیریت موارد خاص
سناریو 1: code خارج از محدوده integer
- عمل:
code = NULLیا استفاده از code جدید - لاگ: ثبت در لاگ
سناریو 2: alias_name خالی
- عمل: استفاده از "شخص بدون نام"
- لاگ: ثبت در لاگ
سناریو 3: person_types خالی
- عمل: پیشفرض
["مشتری"] - لاگ: ثبت در لاگ
سناریو 4: کد تکراری
- عمل: skip کردن با reason "duplicate_code"
- لاگ: ثبت در لاگ
آمار و ارقام
توزیع دادهها
- کل اشخاص: 46,646
- اشخاص قابل انتقال: 44,754 (96.0%)
- کسب و کارهای قابل انتقال: 2,356
- اشخاص با شرکت: 4,250 (9.1%)
- اشخاص با شناسه ملی: 1,072 (2.3%)
- اشخاص با نوع: 19,751 (42.3%)
- اشخاص با چندین نوع: 916 (2.0%)
توزیع انواع شخص
- مشتری: 14,314 (67.0%)
- تامینکننده: 2,705 (12.7%)
- همکار: 2,077 (9.7%)
- کارمند: 1,097 (5.1%)
- فروشنده: 906 (4.2%)
- بازاریاب: 257 (1.2%)
قابل انتقال
- کسب و کارهای منتقل شده: 3,812
- کسب و کارهای دارای شخص که منتقل شدهاند: 2,356
- اشخاص قابل انتقال: 44,754
- اشخاص با code خارج از محدوده integer: 0 (همه code ها در محدوده هستند)
نکات مهم
- حجم متوسط داده: 44,754 شخص - نیاز به پردازش batch به batch
- person_types: باید از جدول
person_person_typeبه JSON array تبدیل شود - alias_name: الزامی است - باید از
nikenameیاnameاستفاده شود - code: باید از bigint به integer تبدیل شود (با بررسی محدوده)
- name splitting: باید به
first_nameوlast_nameتقسیم شود
ریسکها و راهحلها
ریسک 1: code خارج از محدوده integer
راهحل: بررسی محدوده و استفاده از NULL یا code جدید وضعیت: همه code ها در محدوده integer هستند (max: 21,252)
ریسک 2: alias_name خالی
راهحل: استفاده از پیشفرض "شخص بدون نام"
ریسک 3: person_types خالی
راهحل: استفاده از پیشفرض ["مشتری"]
ریسک 4: کدهای تکراری
راهحل: بررسی unique constraint قبل از درج
ریسک 5: اشخاص با چندین نوع
راهحل: تبدیل همه انواع به JSON array
نتیجهگیری
انتقال اشخاص نیاز به:
- ✅ نگاشت business_id
- ✅ تبدیل code (bigint به integer)
- ✅ تبدیل nikename/name به alias_name
- ✅ تقسیم name به first_name و last_name
- ✅ تبدیل person_person_type به person_types (JSON)
- ✅ پردازش batch به batch
تخمین زمان: با batch size 500، حدود 90 batch نیاز است که میتواند 10-20 دقیقه طول بکشد.
آمار دقیق قابل انتقال:
- اشخاص: 44,754
- کسب و کارهای دارای شخص: 2,356
- اشخاص با نوع: 19,751
- اشخاص با چندین نوع: 916