mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 19:11:33 +03:00
ATS-9: update states once template updates (#3496)
Signed-off-by: Vyacheslav Tumanov <me@slavatumanov.me>
This commit is contained in:
parent
f21750a20d
commit
9a7f75c1ed
@ -52,7 +52,8 @@ async function createSpace (tx: TxOperations): Promise<void> {
|
||||
icon: board.component.TemplatesIcon,
|
||||
private: false,
|
||||
archived: false,
|
||||
members: []
|
||||
members: [],
|
||||
attachedToClass: board.class.Board
|
||||
},
|
||||
board.space.BoardTemplates
|
||||
)
|
||||
@ -108,6 +109,13 @@ async function createDefaults (tx: TxOperations): Promise<void> {
|
||||
)
|
||||
}
|
||||
|
||||
async function fixTemplateSpace (tx: TxOperations): Promise<void> {
|
||||
const templateSpace = await tx.findOne(task.class.KanbanTemplateSpace, { _id: board.space.BoardTemplates })
|
||||
if (templateSpace !== undefined && templateSpace?.attachedToClass === undefined) {
|
||||
await tx.update(templateSpace, { attachedToClass: board.class.Board })
|
||||
}
|
||||
}
|
||||
|
||||
async function migrateLabels (client: MigrationClient): Promise<void> {}
|
||||
export const boardOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {
|
||||
@ -116,5 +124,6 @@ export const boardOperation: MigrateOperation = {
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||
const ops = new TxOperations(client, core.account.System)
|
||||
await createDefaults(ops)
|
||||
await fixTemplateSpace(ops)
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,8 @@ async function createSpace (tx: TxOperations): Promise<void> {
|
||||
icon: lead.component.TemplatesIcon,
|
||||
private: false,
|
||||
members: [],
|
||||
archived: false
|
||||
archived: false,
|
||||
attachedToClass: lead.class.Funnel
|
||||
},
|
||||
lead.space.FunnelTemplates
|
||||
)
|
||||
@ -94,10 +95,18 @@ async function createDefaultKanban (tx: TxOperations): Promise<void> {
|
||||
await createKanban(tx, lead.space.DefaultFunnel, defaultTmpl)
|
||||
}
|
||||
|
||||
async function fixTemplateSpace (tx: TxOperations): Promise<void> {
|
||||
const templateSpace = await tx.findOne(task.class.KanbanTemplateSpace, { _id: lead.space.FunnelTemplates })
|
||||
if (templateSpace !== undefined && templateSpace?.attachedToClass === undefined) {
|
||||
await tx.update(templateSpace, { attachedToClass: lead.class.Funnel })
|
||||
}
|
||||
}
|
||||
|
||||
async function createDefaults (tx: TxOperations): Promise<void> {
|
||||
await createSpace(tx)
|
||||
await createSequence(tx, lead.class.Lead)
|
||||
await createDefaultKanban(tx)
|
||||
await fixTemplateSpace(tx)
|
||||
}
|
||||
|
||||
export const leadOperation: MigrateOperation = {
|
||||
|
@ -27,6 +27,14 @@ export const recruitOperation: MigrateOperation = {
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||
const tx = new TxOperations(client, core.account.System)
|
||||
await createDefaults(tx)
|
||||
await fixTemplateSpace(tx)
|
||||
}
|
||||
}
|
||||
|
||||
async function fixTemplateSpace (tx: TxOperations): Promise<void> {
|
||||
const templateSpace = await tx.findOne(task.class.KanbanTemplateSpace, { _id: recruit.space.VacancyTemplates })
|
||||
if (templateSpace !== undefined && templateSpace?.attachedToClass === undefined) {
|
||||
await tx.update(templateSpace, { attachedToClass: recruit.class.Vacancy })
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +156,8 @@ async function createSpaces (tx: TxOperations): Promise<void> {
|
||||
editor: recruit.component.VacancyTemplateEditor,
|
||||
private: false,
|
||||
members: [],
|
||||
archived: false
|
||||
archived: false,
|
||||
attachedToClass: recruit.class.Vacancy
|
||||
},
|
||||
recruit.space.VacancyTemplates
|
||||
)
|
||||
|
@ -14,7 +14,31 @@
|
||||
//
|
||||
|
||||
import { Builder } from '@hcengineering/model'
|
||||
import serverCore from '@hcengineering/server-core'
|
||||
import core from '@hcengineering/core/lib/component'
|
||||
import serverTask from '@hcengineering/server-task'
|
||||
import task from '@hcengineering/task'
|
||||
|
||||
export { serverTaskId } from '@hcengineering/server-task'
|
||||
|
||||
export function createModel (builder: Builder): void {}
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverTask.trigger.OnTemplateStateUpdate,
|
||||
txMatch: {
|
||||
_class: core.class.TxUpdateDoc,
|
||||
objectClass: {
|
||||
$in: [task.class.StateTemplate, task.class.LostStateTemplate, task.class.WonStateTemplate]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverTask.trigger.OnTemplateStateCreate,
|
||||
txMatch: {
|
||||
_class: core.class.TxCreateDoc,
|
||||
objectClass: {
|
||||
$in: [task.class.StateTemplate, task.class.LostStateTemplate, task.class.WonStateTemplate]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -149,7 +149,9 @@ export class TKanban extends TDoc implements Kanban {
|
||||
}
|
||||
|
||||
@Model(task.class.SpaceWithStates, core.class.Space)
|
||||
export class TSpaceWithStates extends TSpace {}
|
||||
export class TSpaceWithStates extends TSpace {
|
||||
templateId!: Ref<KanbanTemplate>
|
||||
}
|
||||
|
||||
@Model(task.class.KanbanTemplateSpace, core.class.Space)
|
||||
export class TKanbanTemplateSpace extends TSpace implements KanbanTemplateSpace {
|
||||
@ -157,13 +159,16 @@ export class TKanbanTemplateSpace extends TSpace implements KanbanTemplateSpace
|
||||
description!: IntlString
|
||||
icon!: AnyComponent
|
||||
editor!: AnyComponent
|
||||
attachedToClass!: Ref<Class<Doc>>
|
||||
}
|
||||
|
||||
@Model(task.class.StateTemplate, core.class.AttachedDoc, DOMAIN_KANBAN)
|
||||
export class TStateTemplate extends TAttachedDoc implements StateTemplate {
|
||||
@Model(task.class.StateTemplate, core.class.Doc, DOMAIN_KANBAN)
|
||||
export class TStateTemplate extends TDoc implements StateTemplate {
|
||||
// We attach to attribute, so we could distinguish between
|
||||
ofAttribute!: Ref<Attribute<Status>>
|
||||
|
||||
attachedTo!: Ref<KanbanTemplate>
|
||||
|
||||
@Prop(TypeString(), task.string.StateTemplateTitle)
|
||||
name!: string
|
||||
|
||||
@ -173,11 +178,13 @@ export class TStateTemplate extends TAttachedDoc implements StateTemplate {
|
||||
declare rank: string
|
||||
}
|
||||
|
||||
@Model(task.class.DoneStateTemplate, core.class.AttachedDoc, DOMAIN_KANBAN)
|
||||
export class TDoneStateTemplate extends TAttachedDoc implements DoneStateTemplate {
|
||||
@Model(task.class.DoneStateTemplate, core.class.Doc, DOMAIN_KANBAN)
|
||||
export class TDoneStateTemplate extends TDoc implements DoneStateTemplate {
|
||||
// We attach to attribute, so we could distinguish between
|
||||
ofAttribute!: Ref<Attribute<Status>>
|
||||
|
||||
attachedTo!: Ref<KanbanTemplate>
|
||||
|
||||
@Prop(TypeString(), task.string.StateTemplateTitle)
|
||||
name!: string
|
||||
|
||||
|
@ -76,25 +76,20 @@ export async function createKanbanTemplate (
|
||||
const doneStateRanks = [...genRanks(data.doneStates.length)]
|
||||
await Promise.all(
|
||||
data.doneStates.map((st, i) =>
|
||||
client.addCollection(
|
||||
st.isWon ? task.class.WonStateTemplate : task.class.LostStateTemplate,
|
||||
data.space,
|
||||
data.kanbanId,
|
||||
task.class.KanbanTemplate,
|
||||
'doneStatesC',
|
||||
{
|
||||
ofAttribute: task.attribute.DoneState,
|
||||
rank: doneStateRanks[i],
|
||||
name: st.name
|
||||
}
|
||||
)
|
||||
client.createDoc(st.isWon ? task.class.WonStateTemplate : task.class.LostStateTemplate, data.space, {
|
||||
rank: doneStateRanks[i],
|
||||
ofAttribute: task.attribute.DoneState,
|
||||
name: st.name,
|
||||
attachedTo: data.kanbanId
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
const stateRanks = [...genRanks(data.states.length)]
|
||||
await Promise.all(
|
||||
data.states.map((st, i) =>
|
||||
client.addCollection(task.class.StateTemplate, data.space, data.kanbanId, task.class.KanbanTemplate, 'statesC', {
|
||||
client.createDoc(task.class.StateTemplate, data.space, {
|
||||
attachedTo: data.kanbanId,
|
||||
ofAttribute: task.attribute.State,
|
||||
rank: stateRanks[i],
|
||||
name: st.name,
|
||||
|
@ -30,7 +30,8 @@ export async function createBoard (
|
||||
description,
|
||||
private: false,
|
||||
archived: false,
|
||||
members: [getCurrentAccount()._id]
|
||||
members: [getCurrentAccount()._id],
|
||||
templateId
|
||||
})
|
||||
|
||||
await Promise.all([createKanban(client, boardRef, templateId)])
|
||||
|
@ -184,7 +184,8 @@
|
||||
archived: false,
|
||||
number: (incResult as any).object.sequence,
|
||||
company,
|
||||
members: [getCurrentAccount()._id]
|
||||
members: [getCurrentAccount()._id],
|
||||
templateId
|
||||
},
|
||||
objectId
|
||||
)
|
||||
|
@ -32,7 +32,6 @@
|
||||
if (template === undefined) {
|
||||
return
|
||||
}
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
showPopup(
|
||||
MessageBox,
|
||||
@ -43,15 +42,7 @@
|
||||
undefined,
|
||||
async (result) => {
|
||||
if (result && template !== undefined) {
|
||||
const collection = hierarchy.isDerived(state._class, task.class.DoneStateTemplate) ? 'doneStatesC' : 'statesC'
|
||||
await client.removeCollection(
|
||||
state._class,
|
||||
template.space,
|
||||
state._id,
|
||||
template._id,
|
||||
template._class,
|
||||
collection
|
||||
)
|
||||
await client.removeDoc(state._class, template.space, state._id)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -57,19 +57,13 @@
|
||||
title: 'New Template'
|
||||
})
|
||||
|
||||
await client.addCollection(
|
||||
task.class.StateTemplate,
|
||||
space as Ref<Doc> as Ref<Space>,
|
||||
template,
|
||||
task.class.KanbanTemplate,
|
||||
'statesC',
|
||||
{
|
||||
ofAttribute: task.attribute.State,
|
||||
name: 'New State',
|
||||
color: 9,
|
||||
rank: [...genRanks(1)][0]
|
||||
}
|
||||
)
|
||||
await client.createDoc(task.class.StateTemplate, space as Ref<Doc> as Ref<Space>, {
|
||||
attachedTo: template,
|
||||
ofAttribute: task.attribute.State,
|
||||
name: 'New State',
|
||||
color: 9,
|
||||
rank: [...genRanks(1)][0]
|
||||
})
|
||||
|
||||
const ranks = [...genRanks(2)]
|
||||
const doneStates = [
|
||||
@ -87,18 +81,12 @@
|
||||
|
||||
await Promise.all(
|
||||
doneStates.map(async (ds) => {
|
||||
await client.addCollection(
|
||||
ds.class,
|
||||
space as Ref<Doc> as Ref<Space>,
|
||||
template,
|
||||
task.class.KanbanTemplate,
|
||||
'doneStatesC',
|
||||
{
|
||||
ofAttribute: task.attribute.DoneState,
|
||||
name: ds.name,
|
||||
rank: ds.rank
|
||||
}
|
||||
)
|
||||
await client.createDoc(ds.class, space as Ref<Doc> as Ref<Space>, {
|
||||
attachedTo: template,
|
||||
ofAttribute: task.attribute.DoneState,
|
||||
name: ds.name,
|
||||
rank: ds.rank
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
|
@ -104,17 +104,19 @@
|
||||
|
||||
if (hierarchy.isDerived(_class, task.class.DoneState)) {
|
||||
const targetClass = _class === task.class.WonState ? task.class.WonStateTemplate : task.class.LostStateTemplate
|
||||
await client.addCollection(targetClass, kanban.space, kanban._id, kanban._class, 'doneStatesC', {
|
||||
await client.createDoc(targetClass, kanban.space, {
|
||||
ofAttribute: task.attribute.DoneState,
|
||||
name: 'New Done State',
|
||||
rank: calcRank(lastOne, undefined)
|
||||
rank: calcRank(lastOne, undefined),
|
||||
attachedTo: kanban._id
|
||||
})
|
||||
} else {
|
||||
await client.addCollection(task.class.StateTemplate, kanban.space, kanban._id, kanban._class, 'statesC', {
|
||||
await client.createDoc(task.class.StateTemplate, kanban.space, {
|
||||
name: 'New State',
|
||||
ofAttribute: task.attribute.DoneState,
|
||||
color: 9,
|
||||
rank: calcRank(lastOne, undefined)
|
||||
rank: calcRank(lastOne, undefined),
|
||||
attachedTo: kanban._id
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,9 @@ export interface DocWithRank extends Doc {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface SpaceWithStates extends Space {}
|
||||
export interface SpaceWithStates extends Space {
|
||||
templateId?: Ref<KanbanTemplate>
|
||||
}
|
||||
|
||||
// S T A T E
|
||||
|
||||
@ -123,12 +125,16 @@ export interface Sequence extends Doc {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface StateTemplate extends AttachedDoc, State {}
|
||||
export interface StateTemplate extends Doc, State {
|
||||
attachedTo: Ref<KanbanTemplate>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface DoneStateTemplate extends AttachedDoc, DoneState {}
|
||||
export interface DoneStateTemplate extends Doc, DoneState {
|
||||
attachedTo: Ref<KanbanTemplate>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -159,6 +165,7 @@ export interface KanbanTemplateSpace extends Space {
|
||||
description: IntlString
|
||||
icon: AnyComponent
|
||||
editor?: AnyComponent
|
||||
attachedToClass: Ref<Class<Doc>>
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,7 +13,95 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Class, Doc, Ref, Space, Tx, TxCreateDoc, TxProcessor, TxUpdateDoc } from '@hcengineering/core'
|
||||
import { TriggerControl } from '@hcengineering/server-core'
|
||||
import core from '@hcengineering/core/lib/component'
|
||||
import task, { KanbanTemplateSpace, KanbanTemplate, StateTemplate, State, DoneState } from '@hcengineering/task'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function OnTemplateStateUpdate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const actualTx = tx as TxUpdateDoc<StateTemplate>
|
||||
const txes = await control.findAll(core.class.TxCollectionCUD, {
|
||||
'tx.objectId': actualTx.objectId
|
||||
})
|
||||
const createTxes = await control.findAll(core.class.TxCreateDoc, { objectId: actualTx.objectId })
|
||||
const updateTxes = await control.findAll(core.class.TxUpdateDoc, { objectId: actualTx.objectId })
|
||||
const prevDoc = TxProcessor.buildDoc2Doc(
|
||||
[...createTxes, ...txes, ...updateTxes].filter((t) => t._id !== tx._id)
|
||||
) as StateTemplate
|
||||
if (prevDoc === undefined) return []
|
||||
const templateSpace = (
|
||||
await control.findAll(task.class.KanbanTemplateSpace, {
|
||||
_id: actualTx.objectSpace as Ref<KanbanTemplateSpace>
|
||||
})
|
||||
)[0] as KanbanTemplateSpace
|
||||
if (templateSpace === undefined) return []
|
||||
const template = (await control.findAll(task.class.KanbanTemplate, { _id: prevDoc.attachedTo }))[0] as KanbanTemplate
|
||||
const classToChange = getClassToChangeOrCreate(actualTx.objectClass)
|
||||
const objectWithStatesToChange = await control.findAll(templateSpace.attachedToClass, { templateId: template._id })
|
||||
const ids = Array.from(objectWithStatesToChange.map((x) => x._id)) as Array<Ref<Space>>
|
||||
const newDoc = TxProcessor.buildDoc2Doc([...createTxes, ...txes, ...updateTxes]) as StateTemplate
|
||||
const statesToChange = Array.from(
|
||||
await control.findAll(classToChange, { space: { $in: ids }, name: prevDoc.name })
|
||||
) as Array<State | DoneState>
|
||||
return statesToChange.map((it) => {
|
||||
const newAttributes = it._class === task.class.State ? { color: newDoc.color, rank: newDoc.rank } : {}
|
||||
return control.txFactory.createTxUpdateDoc(it._class, it.space, it._id, {
|
||||
name: newDoc.name,
|
||||
...newAttributes
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function OnTemplateStateCreate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const actualTx = tx as TxCreateDoc<StateTemplate>
|
||||
const templateSpace = (
|
||||
await control.findAll(task.class.KanbanTemplateSpace, {
|
||||
_id: actualTx.objectSpace as Ref<KanbanTemplateSpace>
|
||||
})
|
||||
)[0] as KanbanTemplateSpace
|
||||
if (templateSpace === undefined) return []
|
||||
const template = (
|
||||
await control.findAll(task.class.KanbanTemplate, { _id: actualTx.attributes.attachedTo })
|
||||
)[0] as KanbanTemplate
|
||||
const classToChange = getClassToChangeOrCreate(actualTx.objectClass)
|
||||
const objectWithStatesToChange = await control.findAll(templateSpace.attachedToClass, { templateId: template._id })
|
||||
const ids = Array.from(objectWithStatesToChange.map((x) => x._id)) as Array<Ref<Space>>
|
||||
const doc = TxProcessor.createDoc2Doc(actualTx)
|
||||
const ofAttribute = classToChange === task.class.State ? task.attribute.State : task.attribute.DoneState
|
||||
return ids.map((it) => {
|
||||
const newAttributes = classToChange === task.class.State ? { color: doc.color, rank: doc.rank } : {}
|
||||
return control.txFactory.createTxCreateDoc(classToChange, it, {
|
||||
ofAttribute,
|
||||
name: doc.name,
|
||||
...newAttributes
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function getClassToChangeOrCreate (check: Ref<Class<Doc>>): Ref<Class<Doc>> {
|
||||
let classToChange = task.class.State
|
||||
switch (check) {
|
||||
case task.class.WonStateTemplate:
|
||||
classToChange = task.class.WonState
|
||||
break
|
||||
case task.class.LostStateTemplate:
|
||||
classToChange = task.class.LostState
|
||||
break
|
||||
}
|
||||
return classToChange
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export default async () => ({
|
||||
function: {}
|
||||
function: {},
|
||||
trigger: {
|
||||
OnTemplateStateUpdate,
|
||||
OnTemplateStateCreate
|
||||
}
|
||||
})
|
||||
|
@ -16,6 +16,7 @@
|
||||
import type { Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import { Presenter } from '@hcengineering/server-notification'
|
||||
import { TriggerFunc } from '@hcengineering/server-core'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -29,5 +30,9 @@ export default plugin(serverTaskId, {
|
||||
function: {
|
||||
IssueHTMLPresenter: '' as Resource<Presenter>,
|
||||
IssueTextPresenter: '' as Resource<Presenter>
|
||||
},
|
||||
trigger: {
|
||||
OnTemplateStateUpdate: '' as Resource<TriggerFunc>,
|
||||
OnTemplateStateCreate: '' as Resource<TriggerFunc>
|
||||
}
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user