Deals Excel import/export aligned with Didar: browser-side import, new deal fields (code, probability, closedAt, creator, lostNote, referrer), Didar custom fields
This commit is contained in:
@@ -96,6 +96,14 @@ const pipelineId = ref('');
|
||||
const ownerId = ref('');
|
||||
const selectedAssigneeIds = ref<string[]>([]);
|
||||
const lostReason = ref('');
|
||||
const lostNote = ref('');
|
||||
const dealCode = ref('');
|
||||
const probability = ref('');
|
||||
const sourceDetail = ref('');
|
||||
const referrer = ref('');
|
||||
const closedAt = ref('');
|
||||
const createdByName = ref('');
|
||||
const sourceRef = ref('');
|
||||
const status = ref('');
|
||||
const customFields = ref<Record<string, unknown>>({});
|
||||
const lines = ref<LineDraft[]>([]);
|
||||
@@ -251,6 +259,14 @@ async function load() {
|
||||
ownerId.value = data.owner?.id ?? auth.user?.id ?? '';
|
||||
selectedAssigneeIds.value = (data.assignees ?? []).map((a) => a.id);
|
||||
lostReason.value = data.lostReason ?? '';
|
||||
lostNote.value = data.lostNote ?? '';
|
||||
dealCode.value = data.code ?? '';
|
||||
probability.value = data.probability == null ? '' : String(data.probability);
|
||||
sourceDetail.value = data.sourceDetail ?? '';
|
||||
referrer.value = data.referrer ?? '';
|
||||
closedAt.value = data.closedAt ? data.closedAt.slice(0, 10) : '';
|
||||
createdByName.value = data.createdBy?.fullName ?? '';
|
||||
sourceRef.value = data.importSource === 'didar' && data.sourceRef ? data.sourceRef : '';
|
||||
status.value = data.status;
|
||||
customFields.value = { ...(data.customFields ?? {}) };
|
||||
lines.value = (data.lineItems ?? []).map((item) => ({
|
||||
@@ -302,6 +318,10 @@ function payload(): CreateDealRequest {
|
||||
? null
|
||||
: Number(amount.value),
|
||||
source: source.value || undefined,
|
||||
sourceDetail: sourceDetail.value.trim() || null,
|
||||
referrer: referrer.value.trim() || null,
|
||||
code: dealCode.value.trim() || null,
|
||||
probability: probability.value === '' ? null : Number(probability.value),
|
||||
notes: notes.value || undefined,
|
||||
tags: parseTags(tagsRaw.value),
|
||||
isStale: health.value === 'stale',
|
||||
@@ -374,6 +394,8 @@ async function submit() {
|
||||
await api.patch(`/deals/${id.value}`, {
|
||||
...payload(),
|
||||
lostReason: lostReason.value || null,
|
||||
lostNote: lostNote.value.trim() || null,
|
||||
closedAt: closedAt.value || null,
|
||||
});
|
||||
if (stageId.value) {
|
||||
await api.patch(`/deals/${id.value}/move`, { stageId: stageId.value });
|
||||
@@ -679,13 +701,43 @@ onMounted(load);
|
||||
<UiInput v-model="source" placeholder="مثال: وبسایت، معرفی، نمایشگاه…" />
|
||||
</UiField>
|
||||
|
||||
<UiField label="شیوه آشنایی دلخواه">
|
||||
<UiInput v-model="sourceDetail" placeholder="توضیح آزاد" />
|
||||
</UiField>
|
||||
|
||||
<UiField label="معرف">
|
||||
<UiInput v-model="referrer" placeholder="نام معرف" />
|
||||
</UiField>
|
||||
|
||||
<UiField label="کد دستی معامله">
|
||||
<UiInput v-model="dealCode" />
|
||||
</UiField>
|
||||
|
||||
<UiField label="احتمال موفقیت (درصد)">
|
||||
<UiInput v-model="probability" type="number" min="0" max="100" />
|
||||
</UiField>
|
||||
|
||||
<UiField label="برچسبها">
|
||||
<UiInput v-model="tagsRaw" placeholder="سازمانی، فوری (با کاما جدا کنید)" />
|
||||
</UiField>
|
||||
|
||||
<UiField v-if="!isNew" label="تاریخ انجام معامله">
|
||||
<JalaliDateInput v-model="closedAt" placeholder="انتخاب تاریخ" />
|
||||
</UiField>
|
||||
|
||||
<UiField v-if="!isNew" label="دلیل شکست (در صورت باخت معامله)" class="crm-col-span-full">
|
||||
<UiInput id="lostReason" v-model="lostReason" placeholder="علت از دست رفتن معامله را بنویسید…" />
|
||||
</UiField>
|
||||
|
||||
<UiField v-if="!isNew" label="یادداشت شکست" class="crm-col-span-full">
|
||||
<UiInput v-model="lostNote" />
|
||||
</UiField>
|
||||
|
||||
<p v-if="!isNew && (createdByName || sourceRef)" class="hint crm-col-span-full">
|
||||
<span v-if="createdByName">ثبتکننده: {{ createdByName }}</span>
|
||||
<span v-if="createdByName && sourceRef"> · </span>
|
||||
<span v-if="sourceRef">کد دیدار: {{ toFaDigits(sourceRef) }}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- بخش محصولات / خدمات -->
|
||||
|
||||
@@ -24,7 +24,22 @@ import UiModal from '@/components/ui/UiModal.vue';
|
||||
import DealFormView from '@/views/DealFormView.vue';
|
||||
import SavedFiltersBar from '@/components/SavedFiltersBar.vue';
|
||||
import type { ImportContactsResult, PipelineBoardDto, PipelineDto } from '@mento/shared';
|
||||
import { brand } from '@mento/shared';
|
||||
import {
|
||||
DEAL_EXCEL_ALIASES,
|
||||
DEAL_EXCEL_COLUMNS,
|
||||
EXCEL_CUSTOM_FIELD_PREFIX,
|
||||
brand,
|
||||
} from '@mento/shared';
|
||||
|
||||
const dealExcelFields = DEAL_EXCEL_COLUMNS.filter((c) => c.key !== '_type').map((c) => ({
|
||||
key: c.key,
|
||||
label: c.header,
|
||||
required: c.key === 'title',
|
||||
}));
|
||||
const dealClientReadOptions = {
|
||||
rawValues: true,
|
||||
customFieldPrefix: EXCEL_CUSTOM_FIELD_PREFIX,
|
||||
};
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import { canManageSettings } from '@/lib/roles';
|
||||
|
||||
@@ -506,6 +521,10 @@ defineExpose({ updateDealStage: applyUpdateDealStage });
|
||||
<ExcelImportFlow
|
||||
preview-path="/deals/import/preview"
|
||||
import-path="/deals/import"
|
||||
rows-import-path="/deals/import/rows"
|
||||
:client-aliases="DEAL_EXCEL_ALIASES"
|
||||
:client-fields="dealExcelFields"
|
||||
:client-read-options="dealClientReadOptions"
|
||||
:disabled="loading || busy"
|
||||
@start="onImportStart"
|
||||
@done="onImportDone"
|
||||
|
||||
Reference in New Issue
Block a user