mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-17 18:05:42 +02:00
fix: allow inactive members in departments (#10983)
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
+17
-2
@@ -404,14 +404,29 @@ export function createModel (builder: Builder): void {
|
||||
attachTo: hr.mixin.Staff,
|
||||
descriptor: view.viewlet.Table,
|
||||
config: [
|
||||
'',
|
||||
{ key: '', props: { showStatus: true } },
|
||||
'department',
|
||||
{
|
||||
key: '$lookup.channels',
|
||||
label: contact.string.ContactInfo,
|
||||
sortingKey: ['$lookup.channels.lastMessage', 'channels']
|
||||
},
|
||||
'modifiedOn'
|
||||
]
|
||||
],
|
||||
viewOptions: {
|
||||
groupBy: [],
|
||||
orderBy: [],
|
||||
other: [
|
||||
{
|
||||
key: 'hideInactive',
|
||||
type: 'toggle',
|
||||
defaultValue: true,
|
||||
actionTarget: 'query',
|
||||
action: contact.function.HideInactive,
|
||||
label: contact.string.HideInactive
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
hr.viewlet.TableMember
|
||||
)
|
||||
|
||||
@@ -136,7 +136,7 @@ async function rebuildDepartmentMembersFromStaff (client: MigrationClient): Prom
|
||||
const persons = await client.find<Person>(DOMAIN_CONTACT, { _class: contact.class.Person })
|
||||
for (const person of persons) {
|
||||
const staff = client.hierarchy.asIf<Person, Staff>(person, hr.mixin.Staff)
|
||||
if (staff?.department === undefined || !staff.active) {
|
||||
if (staff?.department === undefined) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -172,6 +172,10 @@ export const hrOperation: MigrateOperation = {
|
||||
{
|
||||
state: 'rebuildDepartmentMembersFromStaff',
|
||||
func: rebuildDepartmentMembersFromStaff
|
||||
},
|
||||
{
|
||||
state: 'rebuildDepartmentMembersFromStaffIncludingInactive',
|
||||
func: rebuildDepartmentMembersFromStaff
|
||||
}
|
||||
])
|
||||
},
|
||||
|
||||
@@ -78,7 +78,8 @@
|
||||
$: dragPersonId = dragPerson?._id
|
||||
|
||||
$: members = new Set<Ref<Staff>>(value.members as Ref<Staff>[])
|
||||
$: values = allEmployees.filter((it) => members.has(it._id) && it._id !== dragPersonId)
|
||||
$: activeMembers = allEmployees.filter((it) => it.active && members.has(it._id))
|
||||
$: values = activeMembers.filter((it) => it._id !== dragPersonId)
|
||||
|
||||
$: dragging = value._id === dragOver?._id && dragPersonId !== undefined
|
||||
|
||||
@@ -121,7 +122,7 @@
|
||||
<div class="fs-title">
|
||||
{value.name}
|
||||
</div>
|
||||
<Label label={hr.string.MemberCount} params={{ count: value.members.length }} />
|
||||
<Label label={hr.string.MemberCount} params={{ count: activeMembers.length }} />
|
||||
</div>
|
||||
<PersonsPresenter value={values} bind:dragPerson showDragPerson={dragging} />
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
import { Department } from '@hcengineering/hr'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Button, IconAdd, Label, Scroller, Section, eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { Viewlet, ViewletPreference, type ViewOptions } from '@hcengineering/view'
|
||||
import { Table, ViewletSelector, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import hr from '../plugin'
|
||||
import { addMember } from '../utils'
|
||||
@@ -63,6 +63,7 @@
|
||||
let preference: ViewletPreference | undefined
|
||||
let loading = false
|
||||
let viewlet: WithLookup<Viewlet> | undefined
|
||||
let viewOptions: ViewOptions | undefined
|
||||
</script>
|
||||
|
||||
<Section label={hr.string.Members}>
|
||||
@@ -75,7 +76,7 @@
|
||||
bind:loading
|
||||
viewletQuery={{ _id: hr.viewlet.TableMember }}
|
||||
/>
|
||||
<ViewletSettingButton kind={'ghost'} bind:viewlet />
|
||||
<ViewletSettingButton kind={'ghost'} bind:viewlet bind:viewOptions />
|
||||
<Button id={hr.string.AddEmployee} icon={IconAdd} kind={'ghost'} on:click={add} />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
@@ -87,6 +88,8 @@
|
||||
_class={hr.mixin.Staff}
|
||||
config={preference?.config ?? viewlet?.config ?? []}
|
||||
options={viewlet?.options}
|
||||
viewOptionsConfig={viewlet?.viewOptions?.other}
|
||||
{viewOptions}
|
||||
query={{ _id: { $in: members } }}
|
||||
loadingProps={{ length: members.length }}
|
||||
/>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
import { type Department } from '@hcengineering/hr'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Button, IconAdd, Label, Section, showPopup, Scroller } from '@hcengineering/ui'
|
||||
import { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { Viewlet, ViewletPreference, type ViewOptions } from '@hcengineering/view'
|
||||
import { Table, ViewletSelector, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import hr from '../plugin'
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
|
||||
let viewlet: Viewlet | undefined
|
||||
let preference: ViewletPreference | undefined
|
||||
let viewOptions: ViewOptions | undefined
|
||||
</script>
|
||||
|
||||
<Section id="members" label={hr.string.Members} icon={hr.icon.Members}>
|
||||
@@ -72,7 +73,7 @@
|
||||
bind:loading
|
||||
viewletQuery={{ _id: hr.viewlet.TableMember }}
|
||||
/>
|
||||
<ViewletSettingButton kind={'tertiary'} bind:viewlet />
|
||||
<ViewletSettingButton kind={'tertiary'} bind:viewlet bind:viewOptions />
|
||||
{#if !readonly}
|
||||
<Button id={hr.string.AddMember} icon={IconAdd} kind={'ghost'} on:click={createApp} />
|
||||
{/if}
|
||||
@@ -83,9 +84,11 @@
|
||||
{#if members.length > 0 && viewlet}
|
||||
<Scroller horizontal noFade={false}>
|
||||
<Table
|
||||
_class={contact.mixin.Employee}
|
||||
_class={hr.mixin.Staff}
|
||||
config={preference?.config ?? viewlet.config}
|
||||
options={viewlet.options}
|
||||
viewOptionsConfig={viewlet.viewOptions?.other}
|
||||
{viewOptions}
|
||||
query={{ _id: { $in: members } }}
|
||||
loadingProps={{ length: members.length }}
|
||||
{readonly}
|
||||
|
||||
@@ -128,6 +128,7 @@
|
||||
}
|
||||
|
||||
let resultOptions = options
|
||||
let resultQuery: DocumentQuery<Doc> = query
|
||||
|
||||
const update = reduceCalls(async function (
|
||||
_class: Ref<Class<Doc>>,
|
||||
@@ -137,10 +138,12 @@
|
||||
lookup: Lookup<Doc>,
|
||||
associations: AssociationQuery[] | undefined,
|
||||
limit: number,
|
||||
options: FindOptions<Doc> | undefined
|
||||
options: FindOptions<Doc> | undefined,
|
||||
viewOptionsConfig: ViewOptionModel[] | undefined,
|
||||
viewOptions: ViewOptions | undefined
|
||||
) {
|
||||
const p = await getResultQuery(hierarchy, query, viewOptionsConfig, viewOptions)
|
||||
const resultQuery = mergeQueries(p, query)
|
||||
resultQuery = mergeQueries(p, query)
|
||||
loading += q.query(
|
||||
_class,
|
||||
resultQuery,
|
||||
@@ -159,7 +162,18 @@
|
||||
? 1
|
||||
: 0
|
||||
})
|
||||
$: void update(_class, query, _sortKey, sortOrder, lookup, associations, limit, resultOptions)
|
||||
$: void update(
|
||||
_class,
|
||||
query,
|
||||
_sortKey,
|
||||
sortOrder,
|
||||
lookup,
|
||||
associations,
|
||||
limit,
|
||||
resultOptions,
|
||||
viewOptionsConfig,
|
||||
viewOptions
|
||||
)
|
||||
|
||||
$: void getResultOptions(options, viewOptionsConfig, viewOptions).then((p) => {
|
||||
resultOptions = p
|
||||
@@ -170,7 +184,7 @@
|
||||
const qSlow = createQuery()
|
||||
$: qSlow.query(
|
||||
_class,
|
||||
query,
|
||||
resultQuery,
|
||||
(result) => {
|
||||
total = result.total
|
||||
if (totalQuery === undefined) {
|
||||
|
||||
@@ -139,6 +139,7 @@
|
||||
}
|
||||
|
||||
let resultOptions = options
|
||||
let resultQuery: DocumentQuery<Doc> = query
|
||||
|
||||
const update = reduceCalls(async function (
|
||||
_class: Ref<Class<Doc>>,
|
||||
@@ -148,10 +149,12 @@
|
||||
lookup: Lookup<Doc>,
|
||||
associations: AssociationQuery[] | undefined,
|
||||
limit: number,
|
||||
options: FindOptions<Doc> | undefined
|
||||
options: FindOptions<Doc> | undefined,
|
||||
viewOptionsConfig: ViewOptionModel[] | undefined,
|
||||
viewOptions: ViewOptions | undefined
|
||||
) {
|
||||
const p = await getResultQuery(hierarchy, query, viewOptionsConfig, viewOptions)
|
||||
const resultQuery = mergeQueries(p, query)
|
||||
resultQuery = mergeQueries(p, query)
|
||||
loading += q.query(
|
||||
_class,
|
||||
resultQuery,
|
||||
@@ -170,7 +173,18 @@
|
||||
? 1
|
||||
: 0
|
||||
})
|
||||
$: void update(_class, query, _sortKey, sortOrder, lookup, associations, limit, resultOptions)
|
||||
$: void update(
|
||||
_class,
|
||||
query,
|
||||
_sortKey,
|
||||
sortOrder,
|
||||
lookup,
|
||||
associations,
|
||||
limit,
|
||||
resultOptions,
|
||||
viewOptionsConfig,
|
||||
viewOptions
|
||||
)
|
||||
|
||||
$: void getResultOptions(options, viewOptionsConfig, viewOptions).then((p) => {
|
||||
resultOptions = p
|
||||
@@ -181,7 +195,7 @@
|
||||
const qSlow = createQuery()
|
||||
$: qSlow.query(
|
||||
_class,
|
||||
query,
|
||||
resultQuery,
|
||||
(result) => {
|
||||
total = result.total
|
||||
if (totalQuery === undefined) {
|
||||
|
||||
@@ -65,8 +65,9 @@
|
||||
}
|
||||
|
||||
const changeToggle = (model: ViewOptionModel) => {
|
||||
viewOptions[model.key] = !viewOptions[model.key]
|
||||
dispatch('update', { key: model.key, value: viewOptions[model.key] })
|
||||
const value = !(viewOptions[model.key] ?? model.defaultValue)
|
||||
viewOptions[model.key] = value
|
||||
dispatch('update', { key: model.key, value })
|
||||
}
|
||||
|
||||
// checking if selector provides multiple choice options
|
||||
@@ -143,12 +144,16 @@
|
||||
>
|
||||
<span class="overflow-label"><Label label={model.label} /></span>
|
||||
{#if isToggleType(model)}
|
||||
<Toggle
|
||||
on={viewOptions[model.key] ?? model.defaultValue}
|
||||
on:change={() => {
|
||||
changeToggle(model)
|
||||
}}
|
||||
/>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<span on:click|stopPropagation>
|
||||
<Toggle
|
||||
on={viewOptions[model.key] ?? model.defaultValue}
|
||||
on:change={() => {
|
||||
changeToggle(model)
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
{:else if isDropdownType(model)}
|
||||
{@const items = model.values.filter(({ hidden }) => !hidden?.(viewOptions))}
|
||||
<DropdownLabelsIntl
|
||||
|
||||
@@ -196,9 +196,8 @@ export async function OnDepartmentStaff (txes: Tx[], control: TriggerControl): P
|
||||
const removedDepartments = await buildHierarchy(lastDepartment, control)
|
||||
const removed = removedDepartments.map((p) => p._id)
|
||||
const pushIds = push.map((p) => p._id)
|
||||
const added = push.filter((p) => !removed.includes(p._id))
|
||||
const removedIds = exclude(pushIds, removed)
|
||||
result.push(...getTxes(control.txFactory, [employee], added, removedIds))
|
||||
result.push(...getTxes(control.txFactory, [employee], push, removedIds))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,12 +227,10 @@ export async function OnDepartmentUpdate (txes: Tx[], control: TriggerControl):
|
||||
const oldHierarchy = oldParent !== undefined ? await buildHierarchy(oldParent, control) : []
|
||||
const newHierarchy = newParent !== undefined ? await buildHierarchy(newParent, control) : []
|
||||
|
||||
const oldHierarchyIds = oldHierarchy.map((p) => p._id)
|
||||
const newHierarchyIds = newHierarchy.map((p) => p._id)
|
||||
const added = newHierarchy.filter((p) => !oldHierarchyIds.includes(p._id))
|
||||
const removed = oldHierarchy.filter((p) => !newHierarchyIds.includes(p._id)).map((p) => p._id)
|
||||
|
||||
result.push(...getTxes(control.txFactory, members, added, removed))
|
||||
result.push(...getTxes(control.txFactory, members, newHierarchy, removed))
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -309,29 +306,30 @@ export async function OnEmployee (txes: Tx[], control: TriggerControl): Promise<
|
||||
* @public
|
||||
*/
|
||||
export async function OnEmployeeDeactivate (txes: Tx[], control: TriggerControl): Promise<Tx[]> {
|
||||
const result: Tx[] = []
|
||||
for (const tx of txes) {
|
||||
if (core.class.TxMixin !== tx._class) {
|
||||
continue
|
||||
}
|
||||
const ctx = tx as TxMixin<Person, Employee>
|
||||
if (ctx.mixin !== contact.mixin.Employee || ctx.attributes.active !== false) {
|
||||
continue
|
||||
}
|
||||
const employee = ctx.objectId as Ref<Employee>
|
||||
const departments = await control.queryFind(control.ctx, hr.class.Department, {})
|
||||
const removed = departments.filter((dep) => dep.members.some((p) => p === employee))
|
||||
result.push(
|
||||
...getTxes(
|
||||
control.txFactory,
|
||||
[employee],
|
||||
[],
|
||||
removed.map((p) => p._id)
|
||||
)
|
||||
)
|
||||
}
|
||||
return []
|
||||
// const result: Tx[] = []
|
||||
// for (const tx of txes) {
|
||||
// if (core.class.TxMixin !== tx._class) {
|
||||
// continue
|
||||
// }
|
||||
// const ctx = tx as TxMixin<Person, Employee>
|
||||
// if (ctx.mixin !== contact.mixin.Employee || ctx.attributes.active !== false) {
|
||||
// continue
|
||||
// }
|
||||
// const employee = ctx.objectId as Ref<Employee>
|
||||
// const departments = await control.queryFind(control.ctx, hr.class.Department, {})
|
||||
// const removed = departments.filter((dep) => dep.members.some((p) => p === employee))
|
||||
// result.push(
|
||||
// ...getTxes(
|
||||
// control.txFactory,
|
||||
// [employee],
|
||||
// [],
|
||||
// removed.map((p) => p._id)
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
|
||||
return result
|
||||
// return result
|
||||
}
|
||||
|
||||
// TODO: why we need specific email notifications instead of using general flow?
|
||||
|
||||
Reference in New Issue
Block a user