mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-20 22:53:49 +00:00
Merge remote-tracking branch 'origin/master' into issue447_pickCivilityType
This commit is contained in:
@@ -25,7 +25,7 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
use function array_merge;
|
||||
|
||||
|
@@ -45,6 +45,40 @@ use function spl_object_hash;
|
||||
* all users with the right 'CHILL_3PARTY_3PARTY_SEE', 'CHILL_3PARTY_3 to see, select and edit parties for this
|
||||
* center.
|
||||
*
|
||||
* A ThirdParty may have:
|
||||
*
|
||||
* * 0, one or more categories;
|
||||
* * 0, one or more type
|
||||
* * 1 kind.
|
||||
*
|
||||
* ## Kind
|
||||
*
|
||||
* The kind indicate if a thirdparty is a:
|
||||
*
|
||||
* * company ("personne morale");
|
||||
* * a contact ("personne morale")
|
||||
* * a child inside a company ("contact" in French). Only companies may have childs
|
||||
*
|
||||
* **take care** the french translation may lead to misinterpretation, as the string "contact" is the translation of
|
||||
* kind "child".
|
||||
*
|
||||
* ## Categories and types
|
||||
*
|
||||
* ThirdParty may have zero, one or more categories and/or type.
|
||||
*
|
||||
* Categories are managed in the database. The Chill administrator may create or desactivate categories.
|
||||
*
|
||||
* Types are also a way to categorize the thirdparties, but this is done **into the code**, through
|
||||
* @link{Chill\ThirdPartyBundle\ThirdPartyType\ThirdPartyTypeProviderInterface}. The type is stored into the
|
||||
* database by a Php array, mapped by a jsonb into the database. This has one advantage: it is easily searchable.
|
||||
*
|
||||
* As the list of type is hardcoded into database, it is more easily searchable. (for chill 2.0, the
|
||||
* @link{Chill\ThirdPartyBundle\Form\Type\PickThirdPartyDynamicType} does not support it yet, but
|
||||
* the legacy @link{Chill\ThirdPartyBundle\Form\Type\PickThirdPartyType} does.
|
||||
*
|
||||
* The difference between categories and types is transparent for user: they choose the same fields into the UI, without
|
||||
* noticing a difference.
|
||||
*
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="chill_3party.third_party")
|
||||
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||
@@ -179,6 +213,8 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface
|
||||
* @var string
|
||||
* @ORM\Column(name="name", type="string", length=255)
|
||||
* @Assert\Length(min="2")
|
||||
* @Assert\NotNull
|
||||
* @Assert\NotBlank
|
||||
* @Groups({"read", "write", "docgen:read", "docgen:read:3party:parent"})
|
||||
*/
|
||||
private ?string $name = '';
|
||||
|
@@ -22,7 +22,7 @@ use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
use function array_diff;
|
||||
use function array_merge;
|
||||
|
@@ -15,7 +15,7 @@ use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||
use Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter;
|
||||
use Knp\Menu\MenuItem;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* Add an entry in section to go to third party index page.
|
||||
|
@@ -21,57 +21,64 @@
|
||||
</div>
|
||||
<div v-else-if="action === 'edit' || action === 'create'">
|
||||
|
||||
<div class="form-floating mb-3" v-if="thirdparty.kind !== 'child'">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input mt-0" type="radio" v-model="kind" value="company" id="tpartyKindInstitution">
|
||||
<label for="tpartyKindInstitution" class="required">
|
||||
<badge-entity
|
||||
:entity="{ type: 'thirdparty', kind: 'company' }"
|
||||
:options="{ displayLong: true }">
|
||||
</badge-entity>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input mt-0" type="radio" v-model="kind" value="contact" id="tpartyKindContact">
|
||||
<label for="tpartyKindContact" class="required">
|
||||
<badge-entity
|
||||
:entity="{ type: 'thirdparty', kind: 'contact' }"
|
||||
:options="{ displayLong: true }">
|
||||
</badge-entity>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>Contact de :</p>
|
||||
<third-party-render-box :thirdparty="thirdparty.parent"
|
||||
:options="{
|
||||
addInfo: true,
|
||||
addEntity: false,
|
||||
addAltNames: true,
|
||||
addId: false,
|
||||
addLink: false,
|
||||
addAge: false,
|
||||
hLevel: 4,
|
||||
addCenter: false,
|
||||
addNoData: true,
|
||||
isMultiline: false
|
||||
}"></third-party-render-box>
|
||||
</div>
|
||||
<div class="form-floating mb-3" v-if="thirdparty.kind !== 'child'">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input mt-0" type="radio" v-model="kind" value="company" id="tpartyKindInstitution">
|
||||
<label for="tpartyKindInstitution" class="required">
|
||||
<badge-entity
|
||||
:entity="{ type: 'thirdparty', kind: 'company' }"
|
||||
:options="{ displayLong: true }">
|
||||
</badge-entity>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input mt-0" type="radio" v-model="kind" value="contact" id="tpartyKindContact">
|
||||
<label for="tpartyKindContact" class="required">
|
||||
<badge-entity
|
||||
:entity="{ type: 'thirdparty', kind: 'contact' }"
|
||||
:options="{ displayLong: true }">
|
||||
</badge-entity>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>Contact de :</p>
|
||||
<third-party-render-box :thirdparty="thirdparty.parent"
|
||||
:options="{
|
||||
addInfo: true,
|
||||
addEntity: false,
|
||||
addAltNames: true,
|
||||
addId: false,
|
||||
addLink: false,
|
||||
addAge: false,
|
||||
hLevel: 4,
|
||||
addCenter: false,
|
||||
addNoData: true,
|
||||
isMultiline: false
|
||||
}"></third-party-render-box>
|
||||
</div>
|
||||
<div class="form-floating mb-3">
|
||||
<input class="form-control form-control-lg" id="name" v-model="thirdparty.text" v-bind:placeholder="$t('thirdparty.name')" />
|
||||
<label for="name">{{ $t('thirdparty.name') }}</label>
|
||||
</div>
|
||||
<div v-if="query">
|
||||
<ul class="list-suggest add-items inline">
|
||||
<li @click="addQuery(query)">
|
||||
<span class="person-text">{{ query }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<template
|
||||
v-if="thirdparty.kind !== 'child'">
|
||||
<add-address
|
||||
key="thirdparty"
|
||||
:context="context"
|
||||
:options="addAddress.options"
|
||||
:address-changed-callback="submitAddress"
|
||||
ref="addAddress">
|
||||
</add-address>
|
||||
</template>
|
||||
<template
|
||||
v-if="thirdparty.kind !== 'child'">
|
||||
<add-address
|
||||
key="thirdparty"
|
||||
:context="context"
|
||||
:options="addAddress.options"
|
||||
:address-changed-callback="submitAddress"
|
||||
ref="addAddress">
|
||||
</add-address>
|
||||
</template>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text" id="email"><i class="fa fa-fw fa-envelope"></i></span>
|
||||
@@ -102,7 +109,7 @@ import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue';
|
||||
|
||||
export default {
|
||||
name: "OnTheFlyThirdParty",
|
||||
props: ['id', 'type', 'action'],
|
||||
props: ['id', 'type', 'action', 'query'],
|
||||
components: {
|
||||
ThirdPartyRenderBox,
|
||||
AddAddress,
|
||||
@@ -113,6 +120,11 @@ export default {
|
||||
//context: {}, <--
|
||||
thirdparty: {
|
||||
type: 'thirdparty',
|
||||
address: null,
|
||||
kind: 'company',
|
||||
name: '',
|
||||
telephone: '',
|
||||
|
||||
},
|
||||
addAddress: {
|
||||
options: {
|
||||
@@ -122,27 +134,27 @@ export default {
|
||||
size: 'btn-sm'
|
||||
},
|
||||
title: {
|
||||
create: 'add_an_address_title',
|
||||
edit: 'edit_address'
|
||||
create: 'add_an_address_title',
|
||||
edit: 'edit_address'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
kind: {
|
||||
get() {
|
||||
// note: there are also default to 'institution' set in the "mounted" method
|
||||
if (this.$data.thirdparty.kind !== undefined) {
|
||||
return this.$data.thirdparty.kind;
|
||||
} else {
|
||||
return 'company';
|
||||
}
|
||||
},
|
||||
set(v) {
|
||||
this.$data.thirdparty.kind = v;
|
||||
}
|
||||
},
|
||||
kind: {
|
||||
get() {
|
||||
// note: there are also default to 'institution' set in the "mounted" method
|
||||
if (this.$data.thirdparty.kind !== undefined) {
|
||||
return this.$data.thirdparty.kind;
|
||||
} else {
|
||||
return 'company';
|
||||
}
|
||||
},
|
||||
set(v) {
|
||||
this.$data.thirdparty.kind = v;
|
||||
}
|
||||
},
|
||||
context() {
|
||||
let context = {
|
||||
target: {
|
||||
@@ -153,10 +165,10 @@ export default {
|
||||
addressId: null
|
||||
};
|
||||
if ( !(this.thirdparty.address === undefined || this.thirdparty.address === null)
|
||||
&& this.thirdparty.address.address_id !== null
|
||||
&& this.thirdparty.address.address_id !== null
|
||||
) { // to complete
|
||||
context.addressId = this.thirdparty.address.address_id;
|
||||
context.edit = true;
|
||||
context.addressId = this.thirdparty.address.address_id;
|
||||
context.edit = true;
|
||||
}
|
||||
console.log('context', context);
|
||||
//this.context = context; <--
|
||||
@@ -170,10 +182,10 @@ export default {
|
||||
this.thirdparty.kind = thirdparty.kind;
|
||||
//console.log('get thirdparty', thirdparty);
|
||||
if (this.action !== 'show') {
|
||||
if (thirdparty.address !== null) {
|
||||
// bof! we force getInitialAddress because addressId not available when mounted
|
||||
this.$refs.addAddress.getInitialAddress(thirdparty.address.address_id);
|
||||
}
|
||||
if (thirdparty.address !== null) {
|
||||
// bof! we force getInitialAddress because addressId not available when mounted
|
||||
this.$refs.addAddress.getInitialAddress(thirdparty.address.address_id);
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
}));
|
||||
@@ -181,11 +193,14 @@ export default {
|
||||
submitAddress(payload) {
|
||||
console.log('submitAddress', payload);
|
||||
if (typeof payload.addressId !== 'undefined') { // <--
|
||||
this.context.edit = true;
|
||||
this.context.addressId = payload.addressId; // bof! use legacy and not legacy in payload
|
||||
this.thirdparty.address = payload.address; // <--
|
||||
console.log('switch address to edit mode', this.context);
|
||||
}
|
||||
this.context.edit = true;
|
||||
this.context.addressId = payload.addressId; // bof! use legacy and not legacy in payload
|
||||
this.thirdparty.address = payload.address; // <--
|
||||
console.log('switch address to edit mode', this.context);
|
||||
}
|
||||
},
|
||||
addQuery(query) {
|
||||
this.thirdparty.text = query;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -193,7 +208,7 @@ export default {
|
||||
if (this.action !== 'create') {
|
||||
this.loadData();
|
||||
} else {
|
||||
this.thirdparty.kind = 'company';
|
||||
this.thirdparty.kind = 'company';
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@@ -87,14 +87,24 @@ class ThirdPartyApiSearch implements SearchApiInterface
|
||||
foreach ($strs as $str) {
|
||||
if (!empty($str)) {
|
||||
$wheres[] = "(LOWER(UNACCENT(?)) <<% tparty.canonicalized OR
|
||||
tparty.canonicalized LIKE '%' || LOWER(UNACCENT(?)) || '%')";
|
||||
$whereArgs[] = [$str, $str];
|
||||
$pertinence[] = 'STRICT_WORD_SIMILARITY(LOWER(UNACCENT(?)), tparty.canonicalized) + ' .
|
||||
"(tparty.canonicalized LIKE '%s' || LOWER(UNACCENT(?)) || '%')::int + " .
|
||||
tparty.canonicalized LIKE '%' || LOWER(UNACCENT(?)) || '%')
|
||||
OR
|
||||
(LOWER(UNACCENT(?)) <<% parent.canonicalized OR
|
||||
parent.canonicalized LIKE '%' || LOWER(UNACCENT(?)) || '%')
|
||||
";
|
||||
$whereArgs[] = [$str, $str, $str, $str];
|
||||
$pertinence[] = 'GREATEST(
|
||||
STRICT_WORD_SIMILARITY(LOWER(UNACCENT(?)), tparty.canonicalized),
|
||||
STRICT_WORD_SIMILARITY(LOWER(UNACCENT(?)), parent.canonicalized)
|
||||
) + ' .
|
||||
"GREATEST(
|
||||
(tparty.canonicalized LIKE '%s' || LOWER(UNACCENT(?)) || '%')::int,
|
||||
(parent.canonicalized LIKE '%s' || LOWER(UNACCENT(?)) || '%')::int
|
||||
) + " .
|
||||
// take postcode label into account, but lower than the canonicalized field
|
||||
"COALESCE((LOWER(UNACCENT(cmpc.label)) LIKE '%' || LOWER(UNACCENT(?)) || '%')::int * 0.3, 0) + " .
|
||||
"COALESCE((LOWER(UNACCENT(cmpc_p.label)) LIKE '%' || LOWER(UNACCENT(?)) || '%')::int * 0.3, 0)";
|
||||
$pertinenceArgs[] = [$str, $str, $str, $str];
|
||||
$pertinenceArgs[] = [$str, $str, $str, $str, $str, $str];
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
services:
|
||||
Chill\ThirdPartyBundle\Menu\MenuBuilder:
|
||||
arguments:
|
||||
$translator: '@Symfony\Component\Translation\TranslatorInterface'
|
||||
$translator: '@Symfony\Contracts\Translation\TranslatorInterface'
|
||||
$authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface'
|
||||
tags:
|
||||
- { name: 'chill.menu_builder' }
|
||||
|
Reference in New Issue
Block a user