fix for ThirdParty create flow.

- Added `parent` property handling in `ThirdPartyEdit.vue`, `Create.vue`, and related components for better association with parent entities.
- Updated `Thirdparty` and `ThirdPartyWrite` types to include `parent` property and ensure type safety.
- Adjusted translations and messages to reflect the new parent entity association concept.
This commit is contained in:
2025-10-24 16:20:58 +02:00
parent 4234377b7e
commit 71e146e4f0
6 changed files with 111 additions and 86 deletions

View File

@@ -43,6 +43,7 @@
v-if="type === 'thirdparty'" v-if="type === 'thirdparty'"
:action="action" :action="action"
:query="query" :query="query"
:parent="parent"
ref="castThirdparty" ref="castThirdparty"
@onThirdPartyCreated="onThirdPartyCreated" @onThirdPartyCreated="onThirdPartyCreated"
type="" type=""

View File

@@ -53,6 +53,7 @@ defineExpose({ save });
:allowedTypes="props.allowedTypes" :allowedTypes="props.allowedTypes"
:action="props.action" :action="props.action"
:query="props.query" :query="props.query"
:parent="props.parent"
@onPersonCreated="onPersonCreated" @onPersonCreated="onPersonCreated"
@onThirdPartyCreated="onThirdPartyCreated" @onThirdPartyCreated="onThirdPartyCreated"
></Create> ></Create>

View File

@@ -2,7 +2,7 @@ import {
Address, Address,
Center, Center,
Civility, Civility,
DateTime, DateTime, SetCivility,
User, User,
} from "ChillMainAssets/types"; } from "ChillMainAssets/types";
@@ -15,6 +15,7 @@ export interface BaseThirdParty {
acronym: string | null; acronym: string | null;
active: boolean; active: boolean;
address: Address | null; address: Address | null;
contactDataAnonymous: boolean;
createdAt: DateTime; createdAt: DateTime;
createdBy: User | null; createdBy: User | null;
email: string | null; email: string | null;
@@ -22,6 +23,7 @@ export interface BaseThirdParty {
id: number; id: number;
nameCompany: string | null; nameCompany: string | null;
telephone: string | null; telephone: string | null;
telephone2: string | null;
updatedAt: DateTime | null; updatedAt: DateTime | null;
updatedBy: User | null; updatedBy: User | null;
} }
@@ -50,7 +52,7 @@ export interface ThirdpartyChild extends BaseThirdParty {
kind: "child"; kind: "child";
civility: Civility | null; civility: Civility | null;
contactDataAnonymous: boolean; contactDataAnonymous: boolean;
parent: Thirdparty | null; parent: ThirdpartyCompany;
profession: string; profession: string;
firstname: string; firstname: string;
/** /**
@@ -109,10 +111,15 @@ export interface ThirdpartyCategory {
}; };
} }
export interface SetThirdParty {
readonly type: "thirdparty";
id: number;
}
export interface ThirdPartyWrite { export interface ThirdPartyWrite {
readonly type: "thirdparty"; readonly type: "thirdparty";
kind: ThirdPartyKind; kind: ThirdPartyKind;
civility: Civility | null; civility: SetCivility | null;
profession: string; profession: string;
firstname: string; firstname: string;
/** /**
@@ -126,4 +133,5 @@ export interface ThirdPartyWrite {
address_id: number; address_id: number;
} }
comment: string; comment: string;
parent: SetThirdParty|null;
} }

View File

@@ -5,7 +5,7 @@
<div class="item-col"> <div class="item-col">
<div class="entity-label"> <div class="entity-label">
<div :class="'denomination h' + options.hLevel"> <div :class="'denomination h' + options.hLevel">
<a v-if="this.options.addLink === true" href="#"> <a v-if="options.addLink === true" href="#">
<span class="name">{{ thirdparty.text }}</span> <span class="name">{{ thirdparty.text }}</span>
</a> </a>
<span class="name" v-else>{{ thirdparty.text }}</span> <span class="name" v-else>{{ thirdparty.text }}</span>
@@ -27,7 +27,7 @@
/> />
</div> </div>
<p v-if="this.options.addInfo === true" class="moreinfo" /> <p v-if="options.addInfo === true" class="moreinfo" />
</div> </div>
</div> </div>
@@ -44,13 +44,13 @@
<span>{{ getProfession[0] }}</span> <span>{{ getProfession[0] }}</span>
</p> </p>
</li> </li>
<li v-if="hasParent"> <li v-if="isThirdpartyChild(props.thirdparty)">
<i class="fa fa-li fa-hand-o-right" /> <i class="fa fa-li fa-hand-o-right" />
<b class="me-2">{{ $t("child_of") }}</b> <b class="me-2">{{ trans(THIRDPARTY_MESSAGES_CHILD_OF)}}</b>
<on-the-fly <on-the-fly
:type="thirdparty.parent.type" :type="props.thirdparty.parent.type"
:id="thirdparty.parent.id" :id="props.thirdparty.parent.id"
:button-text="thirdparty.parent.text" :button-text="props.thirdparty.parent.text"
:display-badge="'true' === 'true'" :display-badge="'true' === 'true'"
action="show" action="show"
/> />
@@ -128,65 +128,60 @@
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { computed, defineAsyncComponent } from "vue";
import AddressRenderBox from "ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue"; import AddressRenderBox from "ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue";
import Confidential from "ChillMainAssets/vuejs/_components/Confidential.vue"; import Confidential from "ChillMainAssets/vuejs/_components/Confidential.vue";
import BadgeEntity from "ChillMainAssets/vuejs/_components/BadgeEntity.vue"; import BadgeEntity from "ChillMainAssets/vuejs/_components/BadgeEntity.vue";
import {isThirdpartyChild, isThirdpartyCompany, Thirdparty} from "../../../types";
import {localizeString} from "ChillMainAssets/lib/localizationHelper/localizationHelper";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue";
import {
THIRDPARTY_MESSAGES_CHILD_OF,
trans
} from "translator";
export default { // Async to avoid recursive resolution issues
name: "ThirdPartyRenderBox", /*
components: { const OnTheFly = defineAsyncComponent(() =>
AddressRenderBox, import("ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue")
Confidential, );
BadgeEntity, */
},
// To avoid components recursively invoking eachother resolve OnTheFly component here
beforeCreate() {
this.$options.components.OnTheFly =
require("ChillMainAssets/vuejs/OnTheFly/components/OnTheFly").default;
},
i18n: {
messages: {
fr: {
children: "Personnes de contact: ",
child_of: "Contact de: ",
},
},
},
props: ["thirdparty", "options"],
computed: {
isMultiline: function () {
if (this.options.isMultiline) {
return this.options.isMultiline;
} else {
return false;
}
},
hasParent() {
return !(
this.thirdparty.parent === null || this.thirdparty.parent === undefined
);
},
getProfession() {
let profession = [];
if (this.hasParent && this.thirdparty.profession) {
profession.push(this.thirdparty.profession);
return profession;
}
if (!this.hasParent && this.thirdparty.category) { interface RenderOptions {
profession = this.thirdparty.category.map((category) => category.text); addInfo?: boolean;
} addEntity?: boolean;
addId?: boolean;
addLink?: boolean;
hLevel?: number;
entityDisplayLong?: boolean;
isMultiline?: boolean;
}
return profession; const props = defineProps<{ thirdparty: Thirdparty; options: RenderOptions }>();
},
/* TODO need backend normalizer to serve children without circular reference const isMultiline = computed<boolean>(() => props.options?.isMultiline ?? false);
hasChildren() {
//console.log(this.thirdparty.activeChildren.length > 0) const hasParent = computed<boolean>(() => {
return false return isThirdpartyChild(props.thirdparty);
} */ });
},
}; const getProfession = computed<string[]>(() => {
const t = props.thirdparty;
const prof: string[] = [];
if (!isThirdpartyCompany(t)) {
prof.push(t.profession);
}
if (!isThirdpartyChild(t)) {
for (const c of t.categories) {
prof.push(localizeString(c.name));
}
}
return prof;
});
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@@ -7,7 +7,7 @@
<span class="chill-entity badge-thirdparty">{{ parent.text }}</span> <span class="chill-entity badge-thirdparty">{{ parent.text }}</span>
</div> </div>
</div> </div>
<div class="form-floating mb-3" v-else-if="kind !== 'child'"> <div class="form-floating mb-3" v-else-if="props.action !== 'addContact'">
<div class="form-check"> <div class="form-check">
<input <input
class="form-check-input mt-0" class="form-check-input mt-0"
@@ -46,13 +46,9 @@
:options="{ :options="{
addInfo: true, addInfo: true,
addEntity: false, addEntity: false,
addAltNames: true,
addId: false, addId: false,
addLink: false, addLink: false,
addAge: false,
hLevel: 4, hLevel: 4,
addCenter: false,
addNoData: true,
isMultiline: false, isMultiline: false,
}" }"
/> />
@@ -64,7 +60,7 @@
<select <select
class="form-select form-select-lg" class="form-select form-select-lg"
id="civility" id="civility"
v-model="thirdParty.civility" v-model="civility"
> >
<option selected disabled :value="null"> <option selected disabled :value="null">
{{ trans(THIRDPARTY_MESSAGES_THIRDPARTY_CIVILITY) }} {{ trans(THIRDPARTY_MESSAGES_THIRDPARTY_CIVILITY) }}
@@ -72,7 +68,7 @@
<option <option
v-for="civility in civilities" v-for="civility in civilities"
:key="civility.id" :key="civility.id"
:value="civility" :value="civility.id"
> >
{{ localizeString(civility.name) }} {{ localizeString(civility.name) }}
</option> </option>
@@ -210,7 +206,7 @@
/> />
</div> </div>
<div v-if="parent"> <div v-if="props.action !== 'addContact'">
<div class="input-group mb-3"> <div class="input-group mb-3">
<span class="input-group-text" id="comment" <span class="input-group-text" id="comment"
><i class="fa fa-fw fa-pencil" ><i class="fa fa-fw fa-pencil"
@@ -249,21 +245,30 @@ import {
createPerson, createPerson,
getCivilities, WritePersonViolationMap, getCivilities, WritePersonViolationMap,
} from "ChillPersonAssets/vuejs/_api/OnTheFly"; } from "ChillPersonAssets/vuejs/_api/OnTheFly";
import {Thirdparty, ThirdPartyKind, ThirdPartyWrite} from "../../../types"; import {Thirdparty, ThirdpartyCompany, ThirdPartyKind, ThirdPartyWrite} from "../../../types";
import {Civility} from "ChillMainAssets/types"; import {Civility, SetCivility} from "ChillMainAssets/types";
import {isValidationException} from "ChillMainAssets/lib/api/apiMethods"; import {isValidationException} from "ChillMainAssets/lib/api/apiMethods";
interface ThirdPartyEditWriteProps {
interface ThirdPartyEditProps {
id?: number; id?: number;
type?: string|null; type?: 'thirdparty';
action: 'edit' | 'create' | 'addContact'; action: 'edit' | 'create'
query?: string|null; query?: string;
parent?: Thirdparty|null; parent?: null;
} }
const props = withDefaults(defineProps<ThirdPartyEditProps>(), {type: null, query: null, parent: null}); interface ThirdPartyAddContactProps {
id?: null;
type?: 'thirdparty';
action: 'addContact';
query?: "";
parent: ThirdpartyCompany;
}
type ThirdPartyEditProps = ThirdPartyAddContactProps | ThirdPartyEditWriteProps;
const props = withDefaults(defineProps<ThirdPartyEditProps>(), {type: 'thirdparty', query: "", parent: null});
const emit = const emit =
defineEmits<(e: "onThirdPartyCreated", payload: { thirdParty: Thirdparty }) => void>(); defineEmits<(e: "onThirdPartyCreated", payload: { thirdParty: Thirdparty }) => void>();
@@ -282,6 +287,21 @@ const thirdParty = ref<ThirdPartyWrite>({
telephone: "", telephone: "",
telephone2: "", telephone2: "",
comment: "", comment: "",
parent: null,
});
const civility = computed<number|null>({
get: () => {
if (thirdParty.value.civility !== null) {
return thirdParty.value.civility.id;
}
return null;
},
set: (value: number | null) => {
thirdParty.value.civility =
value !== null ? { id: value, type: "chill_main_civility" } : null;
},
}); });
const civilities = ref<Civility[]>([]) const civilities = ref<Civility[]>([])
@@ -352,12 +372,12 @@ onMounted(() => {
getCivilities().then((cv) => { getCivilities().then((cv) => {
civilities.value = cv; civilities.value = cv;
}); });
if (props.action !== "create") { if (props.action === "edit") {
loadData(); loadData();
} } else if (props.action === 'addContact') {
if (props.action === 'addContact') { thirdParty.value.kind = 'child';
thirdParty.value.kind = 'child' thirdParty.value.address = null;
thirdParty.value.address = null thirdParty.value.parent = {id: props.parent.id, type: 'thirdparty'};
} }
}); });

View File

@@ -169,7 +169,7 @@ thirdpartyMessages:
comment: "Commentaire" comment: "Commentaire"
profession: "Qualité" profession: "Qualité"
civility: "Civilité" civility: "Civilité"
child_of: "Contact de: " child_of: "Contact d'une institution"
children: "Personnes de contact: " children: "Personnes de contact: "
thirdparty_duplicate: thirdparty_duplicate: