feat(tahesab): sync full customer profile fields into CRM people

Pull national id, address, city, and rasteh on import/refresh so person records match Tahesab, and surface them on the person form and profile.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-11 18:31:47 +03:30
co-authored by Cursor
parent 8a4bd70ad4
commit d23dd20237
5 changed files with 260 additions and 32 deletions
+41 -1
View File
@@ -97,6 +97,9 @@ const website = ref('');
const instagram = ref('');
const birthDate = ref('');
const referrer = ref('');
const nationalId = ref('');
const city = ref('');
const rasteh = ref('');
const addresses = ref<string[]>(['']);
const notes = ref('');
const tagsRaw = ref('');
@@ -283,6 +286,16 @@ function fill(p: PersonDto) {
mood.value = p.mood || 'newcomer';
groupIds.value = (p.groups ?? []).map((g) => g.id);
customFields.value = { ...(p.customFields ?? {}) };
const cf = customFields.value;
nationalId.value =
typeof cf.nationalId === 'string'
? cf.nationalId
: typeof cf.nationalId === 'number'
? String(cf.nationalId)
: '';
city.value = typeof cf.city === 'string' ? cf.city : '';
rasteh.value =
typeof cf.rasteh === 'string' || typeof cf.rasteh === 'number' ? String(cf.rasteh) : '';
loyaltyPoints.value = p.loyaltyPoints ?? 0;
loyaltyTierName.value = p.loyaltyTier?.name ?? '';
}
@@ -344,6 +357,18 @@ function parseTags(raw: string): string[] {
}
function payload(): CreatePersonRequest {
const fields: Record<string, unknown> = { ...customFields.value };
const nid = nationalId.value.trim();
if (nid) fields.nationalId = nid;
else delete fields.nationalId;
const cityVal = city.value.trim();
if (cityVal) fields.city = cityVal;
else delete fields.city;
const rastehRaw = rasteh.value.trim();
if (rastehRaw) {
const n = Number(rastehRaw);
fields.rasteh = Number.isFinite(n) && String(n) === rastehRaw ? n : rastehRaw;
} else delete fields.rasteh;
return {
firstName: firstName.value,
lastName: lastName.value,
@@ -363,7 +388,7 @@ function payload(): CreatePersonRequest {
companyId: companyId.value || null,
mood: mood.value,
groupIds: groupIds.value,
customFields: customFields.value,
customFields: fields,
};
}
@@ -545,6 +570,21 @@ onMounted(load);
<a-input v-model:value="referrer" placeholder="نام معرف" />
</a-form-item>
</a-col>
<a-col :xs="24" :md="12">
<a-form-item label="کد ملی">
<a-input v-model:value="nationalId" dir="ltr" placeholder="کد ملی (ته‌حساب)" />
</a-form-item>
</a-col>
<a-col :xs="24" :md="12">
<a-form-item label="شهر">
<a-input v-model:value="city" placeholder="شهر" />
</a-form-item>
</a-col>
<a-col :xs="24" :md="12">
<a-form-item label="رسته">
<a-input v-model:value="rasteh" dir="ltr" placeholder="رسته حساب ته‌حساب" />
</a-form-item>
</a-col>
</a-row>
<a-divider orientation="right">آدرس‌ها</a-divider>
+36
View File
@@ -183,6 +183,18 @@ const externalCode = computed(() => {
}
return '';
});
function customFieldText(...keys: string[]) {
const fields = person.value?.customFields ?? {};
for (const key of keys) {
const raw = fields[key];
if (typeof raw === 'string' && raw.trim()) return raw.trim();
if (typeof raw === 'number' && Number.isFinite(raw)) return String(raw);
}
return '';
}
const nationalIdLabel = computed(() => customFieldText('nationalId', 'CodeMelli', 'codeMeli'));
const cityLabel = computed(() => customFieldText('city', 'City'));
const rastehLabel = computed(() => customFieldText('rasteh', 'Rasteh'));
const SOURCE_LABELS: Record<string, string> = {
manual: 'دستی',
tahesab: 'ته‌حساب',
@@ -444,6 +456,9 @@ const infoEmpty = computed(() => {
!p.instagram &&
!p.birthDate &&
!p.referrer &&
!nationalIdLabel.value &&
!cityLabel.value &&
!rastehLabel.value &&
!(p.addresses?.length) &&
!p.company &&
!externalCode.value &&
@@ -794,6 +809,18 @@ watch(() => route.query.tab, readTab, { immediate: true });
<dt>معرف</dt>
<dd>{{ person.referrer }}</dd>
</div>
<div v-if="nationalIdLabel" class="fact">
<dt>کد ملی</dt>
<dd dir="ltr">{{ toFaDigits(nationalIdLabel) }}</dd>
</div>
<div v-if="cityLabel" class="fact">
<dt>شهر</dt>
<dd>{{ cityLabel }}</dd>
</div>
<div v-if="rastehLabel" class="fact">
<dt>رسته</dt>
<dd dir="ltr">{{ toFaDigits(rastehLabel) }}</dd>
</div>
<div v-if="person.website" class="fact">
<dt>وب‌سایت</dt>
<dd dir="ltr">{{ person.website }}</dd>
@@ -1119,6 +1146,15 @@ watch(() => route.query.tab, readTab, { immediate: true });
<div><dt>وب‌سایت</dt><dd>{{ person.website || '—' }}</dd></div>
<div><dt>اینستاگرام</dt><dd dir="ltr">{{ person.instagram || '—' }}</dd></div>
<div><dt>معرف</dt><dd>{{ person.referrer || '—' }}</dd></div>
<div>
<dt>کد ملی</dt>
<dd dir="ltr">{{ nationalIdLabel ? toFaDigits(nationalIdLabel) : '—' }}</dd>
</div>
<div><dt>شهر</dt><dd>{{ cityLabel || '—' }}</dd></div>
<div>
<dt>رسته</dt>
<dd dir="ltr">{{ rastehLabel ? toFaDigits(rastehLabel) : '—' }}</dd>
</div>
<div class="wide">
<dt>آدرس‌ها</dt>
<dd>