changes to import added activity entity
This commit is contained in:
@@ -15,6 +15,7 @@ import Modal from "@/Components/Modal.vue"; // still potentially used elsewhere
|
||||
import CsvPreviewModal from "./Partials/CsvPreviewModal.vue";
|
||||
import SimulationModal from "./Partials/SimulationModal.vue";
|
||||
import { useCurrencyFormat } from "./useCurrencyFormat.js";
|
||||
import DialogModal from "@/Components/DialogModal.vue";
|
||||
|
||||
// Reintroduce props definition lost during earlier edits
|
||||
const props = defineProps({
|
||||
@@ -185,6 +186,23 @@ function downloadUnresolvedCsv() {
|
||||
window.location.href = route("imports.missing-keyref-csv", { import: importId.value });
|
||||
}
|
||||
|
||||
// History import: list of contracts that already existed in DB and were matched
|
||||
const isHistoryImport = computed(() => {
|
||||
const foundList = props.import?.meta?.history_found_contracts;
|
||||
const hasFound = Array.isArray(foundList) && foundList.length > 0;
|
||||
return Boolean(
|
||||
props.import?.template?.meta?.history_import ??
|
||||
props.import?.import_template?.meta?.history_import ??
|
||||
props.import?.meta?.history_import ??
|
||||
hasFound
|
||||
);
|
||||
});
|
||||
const historyFoundContracts = computed(() => {
|
||||
const list = props.import?.meta?.history_found_contracts;
|
||||
return Array.isArray(list) ? list : [];
|
||||
});
|
||||
const showFoundContracts = ref(false);
|
||||
|
||||
// Determine if all detected columns are mapped with entity+field
|
||||
function evaluateMappingSaved() {
|
||||
console.log("here the evaluation happen of mapping save!");
|
||||
@@ -1145,6 +1163,21 @@ async function fetchSimulation() {
|
||||
<div class="py-6">
|
||||
<div class="max-w-5xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white shadow sm:rounded-lg p-6 space-y-6">
|
||||
<div
|
||||
v-if="isHistoryImport || historyFoundContracts.length"
|
||||
class="flex flex-wrap items-center gap-2 text-sm"
|
||||
>
|
||||
<button
|
||||
class="px-3 py-1.5 bg-emerald-700 text-white text-xs rounded"
|
||||
@click.prevent="showFoundContracts = true"
|
||||
title="Prikaži pogodbe, ki so bile najdene in že obstajajo v bazi"
|
||||
>
|
||||
Najdene pogodbe
|
||||
</button>
|
||||
<span v-if="historyFoundContracts.length" class="text-xs text-gray-600">
|
||||
{{ historyFoundContracts.length }} že obstoječih
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="isCompleted" class="p-3 border rounded bg-gray-50 text-sm">
|
||||
<div class="flex flex-wrap gap-x-6 gap-y-1">
|
||||
<div>
|
||||
@@ -1378,6 +1411,45 @@ async function fetchSimulation() {
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<!-- History import: existing contracts found -->
|
||||
<DialogModal :show="showFoundContracts" max-width="3xl" @close="showFoundContracts = false">
|
||||
<template #title>Obstoječe pogodbe najdene v zgodovinskem uvozu</template>
|
||||
<template #content>
|
||||
<div v-if="!historyFoundContracts.length" class="text-sm text-gray-600">Ni zadetkov.</div>
|
||||
<ul v-else class="divide-y divide-gray-200 max-h-[70vh] overflow-auto">
|
||||
<li
|
||||
v-for="item in historyFoundContracts"
|
||||
:key="item.contract_uuid || item.reference"
|
||||
class="py-3 flex items-center justify-between gap-4"
|
||||
>
|
||||
<div class="min-w-0">
|
||||
<div class="font-mono text-sm text-gray-900">{{ item.reference }}</div>
|
||||
<div class="text-xs text-gray-600 truncate">
|
||||
<span>{{ item.full_name || "—" }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-shrink-0">
|
||||
<a
|
||||
v-if="item.case_uuid"
|
||||
:href="route('clientCase.show', { client_case: item.case_uuid })"
|
||||
class="text-blue-600 hover:underline text-xs"
|
||||
>
|
||||
Odpri primer
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<template #footer>
|
||||
<button
|
||||
class="px-3 py-1.5 bg-gray-700 text-white text-xs rounded"
|
||||
@click.prevent="showFoundContracts = false"
|
||||
>
|
||||
Zapri
|
||||
</button>
|
||||
</template>
|
||||
</DialogModal>
|
||||
|
||||
<!-- Unresolved keyref rows modal -->
|
||||
<Modal :show="showUnresolved" max-width="5xl" @close="showUnresolved = false">
|
||||
<div class="p-4 max-h-[75vh] overflow-auto">
|
||||
|
||||
@@ -28,6 +28,8 @@ const form = useForm({
|
||||
delimiter: "",
|
||||
// Payments import mode
|
||||
payments_import: false,
|
||||
// History import mode
|
||||
history_import: false,
|
||||
// For payments mode: how to locate Contract - use single key 'reference'
|
||||
contract_key_mode: null,
|
||||
},
|
||||
@@ -59,6 +61,9 @@ const prevEntities = ref([]);
|
||||
watch(
|
||||
() => form.meta.payments_import,
|
||||
(enabled) => {
|
||||
if (enabled && form.meta.history_import) {
|
||||
form.meta.history_import = false;
|
||||
}
|
||||
if (enabled) {
|
||||
// Save current selection and lock to the required chain
|
||||
prevEntities.value = Array.isArray(form.entities) ? [...form.entities] : [];
|
||||
@@ -74,6 +79,35 @@ watch(
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// History import: restrict entities and auto-add accounts when contracts selected
|
||||
watch(
|
||||
() => form.meta.history_import,
|
||||
(enabled) => {
|
||||
if (enabled && form.meta.payments_import) {
|
||||
form.meta.payments_import = false;
|
||||
form.meta.contract_key_mode = null;
|
||||
}
|
||||
const allowed = ["person", "person_addresses", "person_phones", "contracts", "activities", "client_cases"];
|
||||
if (enabled) {
|
||||
const current = Array.isArray(form.entities) ? [...form.entities] : [];
|
||||
let filtered = current.filter((e) => allowed.includes(e));
|
||||
if (filtered.includes("contracts") && !filtered.includes("accounts")) {
|
||||
filtered = [...filtered, "accounts"];
|
||||
}
|
||||
form.entities = filtered;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => form.entities,
|
||||
(vals) => {
|
||||
if (form.meta.history_import && Array.isArray(vals) && vals.includes("contracts") && ! vals.includes("accounts")) {
|
||||
form.entities = [...vals, "accounts"];
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -112,14 +146,24 @@ watch(
|
||||
<label class="block text-sm font-medium text-gray-700"
|
||||
>Entities (tables)</label
|
||||
>
|
||||
<label class="inline-flex items-center gap-2 text-sm">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="form.meta.payments_import"
|
||||
class="rounded"
|
||||
/>
|
||||
<span>Payments import</span>
|
||||
</label>
|
||||
<div class="flex items-center gap-4 text-sm">
|
||||
<label class="inline-flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="form.meta.history_import"
|
||||
class="rounded"
|
||||
/>
|
||||
<span>History import</span>
|
||||
</label>
|
||||
<label class="inline-flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="form.meta.payments_import"
|
||||
class="rounded"
|
||||
/>
|
||||
<span>Payments import</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="!form.meta.payments_import">
|
||||
<Multiselect
|
||||
@@ -128,11 +172,13 @@ watch(
|
||||
{ value: 'person', label: 'Person' },
|
||||
{ value: 'person_addresses', label: 'Person Addresses' },
|
||||
{ value: 'person_phones', label: 'Person Phones' },
|
||||
{ value: 'client_cases', label: 'Client Cases' },
|
||||
{ value: 'emails', label: 'Emails' },
|
||||
{ value: 'accounts', label: 'Accounts' },
|
||||
{ value: 'contracts', label: 'Contracts' },
|
||||
{ value: 'case_objects', label: 'Case Objects' },
|
||||
{ value: 'payments', label: 'Payments' },
|
||||
{ value: 'activities', label: 'Activities' },
|
||||
]"
|
||||
:multiple="true"
|
||||
track-by="value"
|
||||
@@ -156,6 +202,9 @@ watch(
|
||||
Choose which tables this template targets. You can still define per-column
|
||||
mappings later.
|
||||
</p>
|
||||
<div v-if="form.meta.history_import" class="mt-2 text-xs text-gray-600">
|
||||
History mode allows only person/address/phone/contracts/activities/client cases. Accounts are auto-added when contracts are present and balances stay unchanged.
|
||||
</div>
|
||||
<div v-if="form.meta.payments_import" class="mt-2 text-xs text-gray-600">
|
||||
Payments mode locks entities to:
|
||||
<span class="font-medium">Contracts → Accounts → Payments</span> and
|
||||
|
||||
@@ -26,6 +26,10 @@ const form = useForm({
|
||||
// Add meta with default delimiter support
|
||||
meta: {
|
||||
...(props.template.meta || {}),
|
||||
payments_import: props.template.meta?.payments_import ?? false,
|
||||
history_import: props.template.meta?.history_import ?? false,
|
||||
activity_action_id: props.template.meta?.activity_action_id ?? props.template.meta?.action_id ?? null,
|
||||
activity_decision_id: props.template.meta?.activity_decision_id ?? props.template.meta?.decision_id ?? null,
|
||||
delimiter: (props.template.meta && props.template.meta.delimiter) || "",
|
||||
},
|
||||
});
|
||||
@@ -35,6 +39,21 @@ const decisionsForSelectedAction = vComputed(() => {
|
||||
return act?.decisions || [];
|
||||
});
|
||||
|
||||
const decisionsForActivitiesAction = vComputed(() => {
|
||||
const act = (props.actions || []).find((a) => a.id === form.meta.activity_action_id);
|
||||
return act?.decisions || [];
|
||||
});
|
||||
|
||||
const activityCreatedAtInput = computed({
|
||||
get() {
|
||||
if (!form.meta.activity_created_at) return "";
|
||||
return String(form.meta.activity_created_at).replace(" ", "T");
|
||||
},
|
||||
set(v) {
|
||||
form.meta.activity_created_at = v ? String(v).replace("T", " ") : null;
|
||||
},
|
||||
});
|
||||
|
||||
vWatch(
|
||||
() => form.meta.action_id,
|
||||
() => {
|
||||
@@ -42,6 +61,13 @@ vWatch(
|
||||
}
|
||||
);
|
||||
|
||||
vWatch(
|
||||
() => form.meta.activity_action_id,
|
||||
() => {
|
||||
form.meta.activity_decision_id = null;
|
||||
}
|
||||
);
|
||||
|
||||
const entities = computed(() => props.template.meta?.entities || []);
|
||||
const hasMappings = computed(() => (props.template.mappings?.length || 0) > 0);
|
||||
const canChangeClient = computed(() => !hasMappings.value); // guard reassignment when mappings exist (optional rule)
|
||||
@@ -67,6 +93,15 @@ const unassignedSourceColumns = computed(() => {
|
||||
}
|
||||
return Array.from(set).sort((a, b) => a.localeCompare(b));
|
||||
});
|
||||
const allSourceColumns = computed(() => {
|
||||
const set = new Set();
|
||||
(props.template.sample_headers || []).forEach((h) => set.add(h));
|
||||
(props.template.mappings || []).forEach((m) => {
|
||||
if (m.source_column) set.add(m.source_column);
|
||||
});
|
||||
|
||||
return Array.from(set).sort((a, b) => a.localeCompare(b));
|
||||
});
|
||||
const unassignedState = ref({});
|
||||
|
||||
// Dynamic Import Entity definitions and field options from API
|
||||
@@ -252,6 +287,11 @@ const save = () => {
|
||||
// drop client change when blocked
|
||||
delete payload.client_uuid;
|
||||
}
|
||||
const hasActivities = Array.isArray(payload.meta?.entities) && payload.meta.entities.includes('activities');
|
||||
if (hasActivities && (!payload.meta?.activity_action_id || !payload.meta?.activity_decision_id)) {
|
||||
alert('Activity imports require selecting an Action and Decision (Activities section).');
|
||||
return;
|
||||
}
|
||||
// Normalize empty delimiter: remove from meta to allow auto-detect
|
||||
if (
|
||||
payload.meta &&
|
||||
@@ -300,11 +340,27 @@ watch(
|
||||
watch(
|
||||
() => form.meta.payments_import,
|
||||
(enabled) => {
|
||||
if (enabled) {
|
||||
if (form.meta.history_import) {
|
||||
form.meta.history_import = false;
|
||||
}
|
||||
}
|
||||
if (enabled && !form.meta.contract_key_mode) {
|
||||
form.meta.contract_key_mode = "reference";
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// History mode is mutually exclusive with payments mode
|
||||
watch(
|
||||
() => form.meta.history_import,
|
||||
(enabled) => {
|
||||
if (enabled && form.meta.payments_import) {
|
||||
form.meta.payments_import = false;
|
||||
form.meta.contract_key_mode = null;
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -423,7 +479,7 @@ watch(
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700"
|
||||
>Privzeto Dejanja (Activity)</label
|
||||
>Privzeto Dejanja (post-contract activity)</label
|
||||
>
|
||||
<select
|
||||
v-model="form.meta.action_id"
|
||||
@@ -437,7 +493,7 @@ watch(
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700"
|
||||
>Privzeta Odločitev</label
|
||||
>Privzeta Odločitev (post-contract)</label
|
||||
>
|
||||
<select
|
||||
v-model="form.meta.decision_id"
|
||||
@@ -484,22 +540,31 @@ watch(
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Payments import toggle and settings -->
|
||||
<!-- History / Payments import toggles and settings -->
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
id="payments_import"
|
||||
v-model="form.meta.payments_import"
|
||||
type="checkbox"
|
||||
class="rounded"
|
||||
/>
|
||||
<label for="payments_import" class="text-sm font-medium text-gray-700"
|
||||
>Payments import</label
|
||||
>
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center flex-wrap gap-4">
|
||||
<label class="inline-flex items-center gap-2">
|
||||
<input
|
||||
id="history_import"
|
||||
v-model="form.meta.history_import"
|
||||
type="checkbox"
|
||||
class="rounded"
|
||||
/>
|
||||
<span class="text-sm font-medium text-gray-700">History import</span>
|
||||
</label>
|
||||
<label class="inline-flex items-center gap-2">
|
||||
<input
|
||||
id="payments_import"
|
||||
v-model="form.meta.payments_import"
|
||||
type="checkbox"
|
||||
class="rounded"
|
||||
/>
|
||||
<span class="text-sm font-medium text-gray-700">Payments import</span>
|
||||
</label>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
When enabled, entities are locked to Contracts → Accounts → Payments.
|
||||
<p class="text-xs text-gray-500">
|
||||
History allows person/address/phone/contracts/activities/client cases; accounts are auto-added with contracts. Payments locks entities to Contracts → Accounts → Payments.
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="form.meta.payments_import">
|
||||
@@ -856,6 +921,41 @@ watch(
|
||||
<span class="text-xs text-gray-500">Klikni za razširitev</span>
|
||||
</summary>
|
||||
<div class="mt-4 space-y-4">
|
||||
<div v-if="entity === 'activities'" class="grid grid-cols-1 sm:grid-cols-2 gap-4 p-3 bg-gray-50 rounded border">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Activity action *</label>
|
||||
<select
|
||||
v-model="form.meta.activity_action_id"
|
||||
class="mt-1 block w-full border rounded p-2"
|
||||
required
|
||||
>
|
||||
<option :value="null">(Select action)</option>
|
||||
<option v-for="a in props.actions" :key="a.id" :value="a.id">{{ a.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Activity decision *</label>
|
||||
<select
|
||||
v-model="form.meta.activity_decision_id"
|
||||
class="mt-1 block w-full border rounded p-2"
|
||||
:disabled="!form.meta.activity_action_id"
|
||||
required
|
||||
>
|
||||
<option :value="null">(Select decision)</option>
|
||||
<option v-for="d in decisionsForActivitiesAction" :key="d.id" :value="d.id">{{ d.name }}</option>
|
||||
</select>
|
||||
<p v-if="!form.meta.activity_action_id" class="text-xs text-gray-500 mt-1">Choose an action first.</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Activity created at (override)</label>
|
||||
<input
|
||||
v-model="activityCreatedAtInput"
|
||||
type="datetime-local"
|
||||
class="mt-1 block w-full border rounded p-2"
|
||||
/>
|
||||
<p class="text-xs text-gray-500 mt-1">Optional: set a fixed timestamp for inserted activities (history imports).</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Existing mappings for this entity -->
|
||||
<div
|
||||
v-if="props.template.mappings && props.template.mappings.length"
|
||||
@@ -954,22 +1054,19 @@ watch(
|
||||
<div class="grid grid-cols-1 sm:grid-cols-6 gap-2 items-end">
|
||||
<div>
|
||||
<label class="block text-xs text-gray-600"
|
||||
>Source column (ne-dodeljene)</label
|
||||
>Source column (lahko uporabiš večkrat)</label
|
||||
>
|
||||
<select
|
||||
<input
|
||||
v-model="(newRows[entity] ||= {}).source"
|
||||
class="mt-1 w-full border rounded p-2"
|
||||
>
|
||||
<option value="" disabled>(izberi)</option>
|
||||
<option v-for="s in unassignedSourceColumns" :key="s" :value="s">
|
||||
{{ s }}
|
||||
</option>
|
||||
</select>
|
||||
<p
|
||||
v-if="!unassignedSourceColumns.length"
|
||||
class="text-xs text-gray-500 mt-1"
|
||||
>
|
||||
Ni nedodeljenih virov. Uporabi Bulk ali najprej dodaj vire.
|
||||
list="src-opts-{{ entity }}"
|
||||
placeholder="npr.: note, description"
|
||||
/>
|
||||
<datalist :id="`src-opts-${entity}`">
|
||||
<option v-for="s in allSourceColumns" :key="s" :value="s">{{ s }}</option>
|
||||
</datalist>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
Več stolpcev lahko povežeš na isto polje (npr. activity.note).
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user