@dssp/project 0.0.30 → 0.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/client/pages/lib/select2-component.ts +21 -16
  2. package/client/pages/project/component/project-update-header.ts +16 -13
  3. package/client/pages/project/project-detail.ts +74 -60
  4. package/client/pages/project/project-plan-management.ts +39 -29
  5. package/client/pages/project/project-schedule.ts +4 -3
  6. package/client/pages/project/project-setting-list.ts +9 -9
  7. package/client/pages/project/project-update.ts +53 -29
  8. package/dist-client/pages/lib/select2-component.js +21 -16
  9. package/dist-client/pages/lib/select2-component.js.map +1 -1
  10. package/dist-client/pages/project/component/project-update-header.js +16 -13
  11. package/dist-client/pages/project/component/project-update-header.js.map +1 -1
  12. package/dist-client/pages/project/project-detail.js +74 -60
  13. package/dist-client/pages/project/project-detail.js.map +1 -1
  14. package/dist-client/pages/project/project-plan-management.js +39 -29
  15. package/dist-client/pages/project/project-plan-management.js.map +1 -1
  16. package/dist-client/pages/project/project-schedule.js +4 -3
  17. package/dist-client/pages/project/project-schedule.js.map +1 -1
  18. package/dist-client/pages/project/project-setting-list.js +9 -9
  19. package/dist-client/pages/project/project-setting-list.js.map +1 -1
  20. package/dist-client/pages/project/project-update.js +53 -29
  21. package/dist-client/pages/project/project-update.js.map +1 -1
  22. package/dist-client/tsconfig.tsbuildinfo +1 -1
  23. package/dist-server/controllers/{project-to-excel.js → export-tasks.js} +1 -1
  24. package/dist-server/controllers/export-tasks.js.map +1 -0
  25. package/dist-server/controllers/import-task.d.ts +1 -17
  26. package/dist-server/controllers/import-task.js +24 -14
  27. package/dist-server/controllers/import-task.js.map +1 -1
  28. package/dist-server/controllers/parse-excel.d.ts +4 -0
  29. package/dist-server/controllers/parse-excel.js +75 -0
  30. package/dist-server/controllers/parse-excel.js.map +1 -0
  31. package/dist-server/controllers/types.d.ts +18 -0
  32. package/dist-server/controllers/types.js +3 -0
  33. package/dist-server/controllers/types.js.map +1 -0
  34. package/dist-server/routes.js +2 -2
  35. package/dist-server/routes.js.map +1 -1
  36. package/dist-server/service/project/project-mutation.d.ts +2 -1
  37. package/dist-server/service/project/project-mutation.js +52 -24
  38. package/dist-server/service/project/project-mutation.js.map +1 -1
  39. package/dist-server/service/task/task.d.ts +1 -0
  40. package/dist-server/service/task/task.js +5 -0
  41. package/dist-server/service/task/task.js.map +1 -1
  42. package/dist-server/tsconfig.tsbuildinfo +1 -1
  43. package/package.json +3 -3
  44. package/server/controllers/import-task.ts +25 -31
  45. package/server/controllers/parse-excel.ts +86 -0
  46. package/server/controllers/types.ts +20 -0
  47. package/server/routes.ts +1 -1
  48. package/server/service/project/project-mutation.ts +40 -24
  49. package/server/service/task/task.ts +4 -0
  50. package/dist-server/controllers/project-to-excel.js.map +0 -1
  51. /package/dist-server/controllers/{project-to-excel.d.ts → export-tasks.d.ts} +0 -0
  52. /package/server/controllers/{project-to-excel.ts → export-tasks.ts} +0 -0
@@ -27,4 +27,4 @@ async function generateExcel(tasks) {
27
27
  return await workbook.xlsx.writeBuffer();
28
28
  }
29
29
  exports.generateExcel = generateExcel;
30
- //# sourceMappingURL=project-to-excel.js.map
30
+ //# sourceMappingURL=export-tasks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export-tasks.js","sourceRoot":"","sources":["../../server/controllers/export-tasks.ts"],"names":[],"mappings":";;;AAAA,qCAA6C;AAS7C,SAAS,gBAAgB,CAAC,KAAa,EAAE,SAAoB,EAAE,QAAgB,CAAC;IAC9E,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QACnB,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAA;QAEvE,GAAG,CAAC,YAAY,GAAG,KAAK,CAAA;QAExB,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;YAC7C,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;SACtD;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAEM,KAAK,UAAU,aAAa,CAAC,KAAa;IAC/C,MAAM,QAAQ,GAAG,IAAI,kBAAQ,EAAE,CAAA;IAC/B,MAAM,SAAS,GAAG,QAAQ,CAAC,YAAY,CAAC,aAAa,CAAC,CAAA;IAEtD,SAAS,CAAC,UAAU,CAAC,iBAAiB,GAAG;QACvC,YAAY,EAAE,KAAK;QACnB,YAAY,EAAE,KAAK;KACpB,CAAA;IAED,SAAS,CAAC,OAAO,GAAG;QAClB,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE;QAC/C,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,EAAE;QACrD,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE;KAClD,CAAA;IAED,gBAAgB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;IAElC,OAAO,MAAM,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAA;AAC1C,CAAC;AAlBD,sCAkBC","sourcesContent":["import { Workbook, Worksheet } from 'exceljs'\n\nexport interface Task {\n name: string\n startDate: Date\n endDate: Date\n subtasks?: Task[]\n}\n\nfunction createGanttChart(tasks: Task[], worksheet: Worksheet, level: number = 0) {\n tasks.forEach(task => {\n const row = worksheet.addRow([task.name, task.startDate, task.endDate])\n\n row.outlineLevel = level\n\n if (task.subtasks && task.subtasks.length > 0) {\n createGanttChart(task.subtasks, worksheet, level + 1)\n }\n })\n}\n\nexport async function generateExcel(tasks: Task[]) {\n const workbook = new Workbook()\n const worksheet = workbook.addWorksheet('Gantt Chart')\n\n worksheet.properties.outlineProperties = {\n summaryBelow: false,\n summaryRight: false\n }\n\n worksheet.columns = [\n { header: 'Task Name', key: 'name', width: 30 },\n { header: 'Start Date', key: 'startDate', width: 20 },\n { header: 'End Date', key: 'endDate', width: 20 }\n ]\n\n createGanttChart(tasks, worksheet)\n\n return await workbook.xlsx.writeBuffer()\n}\n"]}
@@ -1,19 +1,3 @@
1
1
  import { Project } from '../service/project/project';
2
- import { TaskType } from '../service/task/task';
3
- export interface RawResource {
4
- type: string;
5
- allocated: number;
6
- }
7
- export interface RawTask {
8
- code: string;
9
- title: string;
10
- type?: TaskType;
11
- duration?: number;
12
- startDate?: string;
13
- dependsOn?: string;
14
- progress?: number;
15
- tags?: string[];
16
- resources?: RawResource[];
17
- children?: RawTask[];
18
- }
2
+ import { RawTask } from './types';
19
3
  export declare function importTasks(project: Project, tasks: RawTask[], context: ResolverContext): Promise<void>;
@@ -5,6 +5,13 @@ const shell_1 = require("@things-factory/shell");
5
5
  const task_1 = require("../service/task/task");
6
6
  const task_resource_1 = require("../service/task-resource/task-resource");
7
7
  const resource_1 = require("../service/resource/resource");
8
+ function excelSerialToJSDate(serial) {
9
+ const excelEpoch = new Date(1899, 11, 30); // Excel epoch (30th December 1899)
10
+ const days = Math.floor(serial); // Get the number of days
11
+ const milliseconds = (serial - days) * 86400 * 1000; // Convert the fractional day part to milliseconds
12
+ const jsDate = new Date(excelEpoch.getTime() + days * 86400 * 1000 + milliseconds);
13
+ return jsDate;
14
+ }
8
15
  async function importTasks(project, tasks, context) {
9
16
  const { domain, user, tx } = context.state;
10
17
  const taskRepository = (0, shell_1.getRepository)(task_1.Task, tx);
@@ -27,20 +34,24 @@ async function importTasks(project, tasks, context) {
27
34
  }
28
35
  if (rawTask.type == task_1.TaskType.TASK) {
29
36
  // 시작일, 종료일 계산
30
- var startDate = rawTask.startDate ? new Date(rawTask.startDate) : undefined;
37
+ var startDate = new Date(rawTask.startDate);
38
+ var endDate;
31
39
  if (!startDate && rawTask.dependsOn) {
32
40
  const dependsOnTask = await taskRepository.findOne({ where: { code: rawTask.dependsOn, project: { id: project.id } } });
33
- if (!dependsOnTask || !dependsOnTask.endDate) {
34
- throw new Error(`Task '${rawTask.code}' depends on a task '${rawTask.dependsOn}' that doesn't have a valid end date.`);
41
+ if (dependsOnTask && dependsOnTask.endDate) {
42
+ startDate = new Date(dependsOnTask.endDate);
43
+ startDate.setDate(startDate.getDate() + 1);
44
+ }
45
+ else {
46
+ // TODO handler error
47
+ // throw new Error(`Task '${rawTask.code}' depends on a task '${rawTask.dependsOn}' that doesn't have a valid end date.`)
35
48
  }
36
- startDate = new Date(dependsOnTask.endDate);
37
- startDate.setDate(startDate.getDate() + 1);
38
49
  }
39
50
  if (!startDate) {
40
51
  throw new Error(`Task '${rawTask.code}' must have either a start date or a valid dependency.`);
41
52
  }
42
- var duration = rawTask.duration;
43
- var endDate = new Date(startDate);
53
+ const duration = rawTask.duration;
54
+ endDate = new Date(startDate);
44
55
  endDate.setDate(startDate.getDate() + duration - 1);
45
56
  }
46
57
  // 태스크 생성 및 저장
@@ -52,10 +63,11 @@ async function importTasks(project, tasks, context) {
52
63
  endDate,
53
64
  project,
54
65
  parent,
55
- duration,
66
+ duration: rawTask.duration,
56
67
  dependsOn: rawTask.dependsOn,
57
68
  progress: rawTask.progress,
58
69
  tags: rawTask.tags,
70
+ style: rawTask.style,
59
71
  updater: user,
60
72
  creator: user
61
73
  });
@@ -76,9 +88,9 @@ async function importTasks(project, tasks, context) {
76
88
  }
77
89
  }
78
90
  // 자식 태스크 처리
79
- if (rawTask.children) {
80
- var lastEndDate = null;
81
- var lastStartDate = null;
91
+ if (rawTask.children && rawTask.children.length > 0) {
92
+ let lastEndDate = null;
93
+ let lastStartDate = null;
82
94
  for (const childTask of rawTask.children) {
83
95
  const subtask = await importTaskData(childTask, task);
84
96
  lastEndDate = !lastEndDate ? subtask.endDate : lastEndDate > subtask.endDate ? lastEndDate : subtask.endDate;
@@ -88,9 +100,7 @@ async function importTasks(project, tasks, context) {
88
100
  const calculatedDuration = lastEndDate && lastStartDate
89
101
  ? Math.ceil((lastEndDate.getTime() - lastStartDate.getTime()) / (1000 * 60 * 60 * 24)) + 1
90
102
  : 0;
91
- task = (await taskRepository.findOne({
92
- where: { id: task.id }
93
- }));
103
+ task = await taskRepository.findOne({ where: { id: task.id } });
94
104
  return await taskRepository.save(Object.assign(Object.assign({}, task), { startDate: lastStartDate, endDate: lastEndDate, duration: calculatedDuration }));
95
105
  }
96
106
  return task;
@@ -1 +1 @@
1
- {"version":3,"file":"import-task.js","sourceRoot":"","sources":["../../server/controllers/import-task.ts"],"names":[],"mappings":"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","sourcesContent":["import { getRepository } from '@things-factory/shell'\n\nimport { Project } from '../service/project/project'\nimport { Task, TaskType } from '../service/task/task'\nimport { TaskResource } from '../service/task-resource/task-resource'\nimport { Resource } from '../service/resource/resource'\n\nexport interface RawResource {\n type: string\n allocated: number\n}\n\nexport interface RawTask {\n code: string\n title: string\n type?: TaskType\n duration?: number\n startDate?: string /* YYYY-MM-DD */\n dependsOn?: string\n progress?: number\n tags?: string[]\n resources?: RawResource[]\n children?: RawTask[]\n}\n\nexport async function importTasks(project: Project, tasks: RawTask[], context: ResolverContext) {\n const { domain, user, tx } = context.state\n\n const taskRepository = getRepository(Task, tx)\n const resourceRepository = getRepository(Resource, tx)\n const taskResourceRepository = getRepository(TaskResource, tx)\n\n // 1. 기존 태스크와 리소스를 Soft Delete\n await taskRepository.softDelete({ project: { id: project.id } })\n\n // 2. 태스크 임포트\n\n const importTaskData = async (rawTask: RawTask, parent?: Task) => {\n if (rawTask.children && rawTask.children.length > 0) {\n rawTask.type = TaskType.GROUP\n } else {\n rawTask.type = TaskType.TASK\n }\n\n // 유효성 검사\n if (!rawTask.title || !rawTask.code || (rawTask.type == TaskType.TASK && (rawTask.duration ?? null) === null)) {\n throw new Error(`Task '${rawTask.code}' is missing required fields.`)\n }\n\n if (rawTask.type == TaskType.TASK) {\n // 시작일, 종료일 계산\n var startDate: Date | undefined = rawTask.startDate ? new Date(rawTask.startDate) : undefined\n if (!startDate && rawTask.dependsOn) {\n const dependsOnTask = await taskRepository.findOne({ where: { code: rawTask.dependsOn, project: { id: project.id } } })\n if (!dependsOnTask || !dependsOnTask.endDate) {\n throw new Error(`Task '${rawTask.code}' depends on a task '${rawTask.dependsOn}' that doesn't have a valid end date.`)\n }\n startDate = new Date(dependsOnTask.endDate)\n startDate.setDate(startDate.getDate() + 1)\n }\n\n if (!startDate) {\n throw new Error(`Task '${rawTask.code}' must have either a start date or a valid dependency.`)\n }\n\n var duration = rawTask.duration\n var endDate = new Date(startDate)\n endDate.setDate(startDate.getDate() + duration - 1)\n }\n\n // 태스크 생성 및 저장\n var task = await taskRepository.save({\n code: rawTask.code,\n name: rawTask.title,\n type: rawTask.type,\n startDate,\n endDate,\n project,\n parent,\n duration,\n dependsOn: rawTask.dependsOn,\n progress: rawTask.progress,\n tags: rawTask.tags,\n updater: user,\n creator: user\n })\n\n // 리소스 생성 및 저장\n if (rawTask.resources) {\n for (const resource of rawTask.resources) {\n const resourceType = await resourceRepository.findOne({ where: { domain: { id: domain.id }, name: resource.type } })\n if (resourceType) {\n await taskResourceRepository.save({\n task,\n resource: resourceType,\n quantity: resource.allocated\n })\n } else {\n throw new Error(`unknown resource type: ${resource.type}`)\n }\n }\n }\n\n // 자식 태스크 처리\n if (rawTask.children) {\n var lastEndDate = null\n var lastStartDate = null\n for (const childTask of rawTask.children) {\n const subtask = await importTaskData(childTask, task)\n\n lastEndDate = !lastEndDate ? subtask.endDate : lastEndDate > subtask.endDate ? lastEndDate : subtask.endDate\n lastStartDate = !lastStartDate ? subtask.startDate : lastStartDate < subtask.startDate ? lastStartDate : subtask.startDate\n }\n\n // 그룹 태스크의 기간(duration)을 계산합니다.\n const calculatedDuration =\n lastEndDate && lastStartDate\n ? Math.ceil((lastEndDate.getTime() - lastStartDate.getTime()) / (1000 * 60 * 60 * 24)) + 1\n : 0\n\n task = (await taskRepository.findOne({\n where: { id: task.id }\n })) as any\n\n return await taskRepository.save({\n ...task,\n startDate: lastStartDate,\n endDate: lastEndDate,\n duration: calculatedDuration\n })\n }\n\n return task\n }\n\n // 루트 태스크들 임포트\n for (const rootTask of tasks) {\n await importTaskData(rootTask)\n }\n}\n"]}
1
+ {"version":3,"file":"import-task.js","sourceRoot":"","sources":["../../server/controllers/import-task.ts"],"names":[],"mappings":";;;AAAA,iDAAqD;AAGrD,+CAAqD;AACrD,0EAAqE;AACrE,2DAAuD;AAGvD,SAAS,mBAAmB,CAAC,MAAM;IACjC,MAAM,UAAU,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA,CAAC,mCAAmC;IAC7E,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA,CAAC,yBAAyB;IACzD,MAAM,YAAY,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,KAAK,GAAG,IAAI,CAAA,CAAC,kDAAkD;IAEtG,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,YAAY,CAAC,CAAA;IAClF,OAAO,MAAM,CAAA;AACf,CAAC;AAEM,KAAK,UAAU,WAAW,CAAC,OAAgB,EAAE,KAAgB,EAAE,OAAwB;IAC5F,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAE1C,MAAM,cAAc,GAAG,IAAA,qBAAa,EAAC,WAAI,EAAE,EAAE,CAAC,CAAA;IAC9C,MAAM,kBAAkB,GAAG,IAAA,qBAAa,EAAC,mBAAQ,EAAE,EAAE,CAAC,CAAA;IACtD,MAAM,sBAAsB,GAAG,IAAA,qBAAa,EAAC,4BAAY,EAAE,EAAE,CAAC,CAAA;IAE9D,8BAA8B;IAC9B,MAAM,cAAc,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;IAEhE,aAAa;IACb,MAAM,cAAc,GAAG,KAAK,EAAE,OAAgB,EAAE,MAAa,EAAE,EAAE;;QAC/D,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;YACnD,OAAO,CAAC,IAAI,GAAG,eAAQ,CAAC,KAAK,CAAA;SAC9B;aAAM;YACL,OAAO,CAAC,IAAI,GAAG,eAAQ,CAAC,IAAI,CAAA;SAC7B;QAED,SAAS;QACT,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,eAAQ,CAAC,IAAI,IAAI,CAAC,MAAA,OAAO,CAAC,QAAQ,mCAAI,IAAI,CAAC,KAAK,IAAI,CAAC,EAAE;YAC7G,MAAM,IAAI,KAAK,CAAC,SAAS,OAAO,CAAC,IAAI,+BAA+B,CAAC,CAAA;SACtE;QAED,IAAI,OAAO,CAAC,IAAI,IAAI,eAAQ,CAAC,IAAI,EAAE;YACjC,cAAc;YACd,IAAI,SAAS,GAAS,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;YAEjD,IAAI,OAAO,CAAA;YACX,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,EAAE;gBACnC,MAAM,aAAa,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;gBACvH,IAAI,aAAa,IAAI,aAAa,CAAC,OAAO,EAAE;oBAC1C,SAAS,GAAG,IAAI,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;oBAC3C,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAA;iBAC3C;qBAAM;oBACL,qBAAqB;oBACrB,yHAAyH;iBAC1H;aACF;YAED,IAAI,CAAC,SAAS,EAAE;gBACd,MAAM,IAAI,KAAK,CAAC,SAAS,OAAO,CAAC,IAAI,wDAAwD,CAAC,CAAA;aAC/F;YAED,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAA;YACjC,OAAO,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAA;YAC7B,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAA;SACpD;QAED,cAAc;QACd,IAAI,IAAI,GAAS,MAAM,cAAc,CAAC,IAAI,CAAC;YACzC,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,IAAI,EAAE,OAAO,CAAC,KAAK;YACnB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,SAAS;YACT,OAAO;YACP,OAAO;YACP,MAAM;YACN,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;SACd,CAAC,CAAA;QAEF,cAAc;QACd,IAAI,OAAO,CAAC,SAAS,EAAE;YACrB,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,SAAS,EAAE;gBACxC,MAAM,YAAY,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;gBACpH,IAAI,YAAY,EAAE;oBAChB,MAAM,sBAAsB,CAAC,IAAI,CAAC;wBAChC,IAAI;wBACJ,QAAQ,EAAE,YAAY;wBACtB,QAAQ,EAAE,QAAQ,CAAC,SAAS;qBAC7B,CAAC,CAAA;iBACH;qBAAM;oBACL,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;iBAC3D;aACF;SACF;QAED,YAAY;QACZ,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;YACnD,IAAI,WAAW,GAAG,IAAI,CAAA;YACtB,IAAI,aAAa,GAAG,IAAI,CAAA;YACxB,KAAK,MAAM,SAAS,IAAI,OAAO,CAAC,QAAQ,EAAE;gBACxC,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;gBAErD,WAAW,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAA;gBAC5G,aAAa,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAA;aAC3H;YAED,+BAA+B;YAC/B,MAAM,kBAAkB,GACtB,WAAW,IAAI,aAAa;gBAC1B,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,aAAa,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC;gBAC1F,CAAC,CAAC,CAAC,CAAA;YAEP,IAAI,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;YAE/D,OAAO,MAAM,cAAc,CAAC,IAAI,iCAC3B,IAAI,KACP,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,kBAAkB,IAC5B,CAAA;SACH;QAED,OAAO,IAAI,CAAA;IACb,CAAC,CAAA;IAED,cAAc;IACd,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE;QAC5B,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAA;KAC/B;AACH,CAAC;AApHD,kCAoHC","sourcesContent":["import { getRepository } from '@things-factory/shell'\n\nimport { Project } from '../service/project/project'\nimport { Task, TaskType } from '../service/task/task'\nimport { TaskResource } from '../service/task-resource/task-resource'\nimport { Resource } from '../service/resource/resource'\nimport { RawTask } from './types'\n\nfunction excelSerialToJSDate(serial) {\n const excelEpoch = new Date(1899, 11, 30) // Excel epoch (30th December 1899)\n const days = Math.floor(serial) // Get the number of days\n const milliseconds = (serial - days) * 86400 * 1000 // Convert the fractional day part to milliseconds\n\n const jsDate = new Date(excelEpoch.getTime() + days * 86400 * 1000 + milliseconds)\n return jsDate\n}\n\nexport async function importTasks(project: Project, tasks: RawTask[], context: ResolverContext) {\n const { domain, user, tx } = context.state\n\n const taskRepository = getRepository(Task, tx)\n const resourceRepository = getRepository(Resource, tx)\n const taskResourceRepository = getRepository(TaskResource, tx)\n\n // 1. 기존 태스크와 리소스를 Soft Delete\n await taskRepository.softDelete({ project: { id: project.id } })\n\n // 2. 태스크 임포트\n const importTaskData = async (rawTask: RawTask, parent?: Task) => {\n if (rawTask.children && rawTask.children.length > 0) {\n rawTask.type = TaskType.GROUP\n } else {\n rawTask.type = TaskType.TASK\n }\n\n // 유효성 검사\n if (!rawTask.title || !rawTask.code || (rawTask.type == TaskType.TASK && (rawTask.duration ?? null) === null)) {\n throw new Error(`Task '${rawTask.code}' is missing required fields.`)\n }\n\n if (rawTask.type == TaskType.TASK) {\n // 시작일, 종료일 계산\n var startDate: Date = new Date(rawTask.startDate)\n\n var endDate\n if (!startDate && rawTask.dependsOn) {\n const dependsOnTask = await taskRepository.findOne({ where: { code: rawTask.dependsOn, project: { id: project.id } } })\n if (dependsOnTask && dependsOnTask.endDate) {\n startDate = new Date(dependsOnTask.endDate)\n startDate.setDate(startDate.getDate() + 1)\n } else {\n // TODO handler error\n // throw new Error(`Task '${rawTask.code}' depends on a task '${rawTask.dependsOn}' that doesn't have a valid end date.`)\n }\n }\n\n if (!startDate) {\n throw new Error(`Task '${rawTask.code}' must have either a start date or a valid dependency.`)\n }\n\n const duration = rawTask.duration\n endDate = new Date(startDate)\n endDate.setDate(startDate.getDate() + duration - 1)\n }\n\n // 태스크 생성 및 저장\n var task: Task = await taskRepository.save({\n code: rawTask.code,\n name: rawTask.title,\n type: rawTask.type,\n startDate,\n endDate,\n project,\n parent,\n duration: rawTask.duration,\n dependsOn: rawTask.dependsOn,\n progress: rawTask.progress,\n tags: rawTask.tags,\n style: rawTask.style,\n updater: user,\n creator: user\n })\n\n // 리소스 생성 및 저장\n if (rawTask.resources) {\n for (const resource of rawTask.resources) {\n const resourceType = await resourceRepository.findOne({ where: { domain: { id: domain.id }, name: resource.type } })\n if (resourceType) {\n await taskResourceRepository.save({\n task,\n resource: resourceType,\n quantity: resource.allocated\n })\n } else {\n throw new Error(`unknown resource type: ${resource.type}`)\n }\n }\n }\n\n // 자식 태스크 처리\n if (rawTask.children && rawTask.children.length > 0) {\n let lastEndDate = null\n let lastStartDate = null\n for (const childTask of rawTask.children) {\n const subtask = await importTaskData(childTask, task)\n\n lastEndDate = !lastEndDate ? subtask.endDate : lastEndDate > subtask.endDate ? lastEndDate : subtask.endDate\n lastStartDate = !lastStartDate ? subtask.startDate : lastStartDate < subtask.startDate ? lastStartDate : subtask.startDate\n }\n\n // 그룹 태스크의 기간(duration)을 계산합니다.\n const calculatedDuration =\n lastEndDate && lastStartDate\n ? Math.ceil((lastEndDate.getTime() - lastStartDate.getTime()) / (1000 * 60 * 60 * 24)) + 1\n : 0\n\n task = await taskRepository.findOne({ where: { id: task.id } })\n\n return await taskRepository.save({\n ...task,\n startDate: lastStartDate,\n endDate: lastEndDate,\n duration: calculatedDuration\n })\n }\n\n return task\n }\n\n // 루트 태스크들 임포트\n for (const rootTask of tasks) {\n await importTaskData(rootTask)\n }\n}\n"]}
@@ -0,0 +1,4 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import { Project } from '../service/project/project';
4
+ export declare function parseExcelAndImportTasks(buffer: Buffer, project: Project, context: ResolverContext): Promise<void>;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseExcelAndImportTasks = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const exceljs_1 = tslib_1.__importDefault(require("exceljs"));
6
+ const import_task_1 = require("./import-task");
7
+ async function parseExcelAndImportTasks(buffer, project, context) {
8
+ // 1. 엑셀 파일을 읽어들입니다.
9
+ const workbook = new exceljs_1.default.Workbook();
10
+ await workbook.xlsx.load(buffer);
11
+ // 2. 첫 번째 워크시트를 가져옵니다.
12
+ const worksheet = workbook.getWorksheet(1); // Index or sheet name can be used
13
+ // 3. 첫 번째 row를 header로 사용합니다.
14
+ const headers = [];
15
+ let taskCodeColumnIndex = -1;
16
+ const headerRow = worksheet.getRow(1);
17
+ headerRow.eachCell((cell, colNumber) => {
18
+ const headerText = cell.text.toString();
19
+ headers[colNumber - 1] = headerText; // Store headers in an array
20
+ if (headerText === '작업코드') {
21
+ taskCodeColumnIndex = colNumber; // Store the column index for "작업코드"
22
+ }
23
+ });
24
+ if (taskCodeColumnIndex === -1) {
25
+ throw new Error('작업코드 column not found');
26
+ }
27
+ // 4. 엑셀 데이터를 RawTask 형식으로 변환합니다.
28
+ const tasks = [];
29
+ // Start processing from the second row onward to skip the header
30
+ for (let rowIndex = 2; rowIndex <= worksheet.rowCount; rowIndex++) {
31
+ const row = worksheet.getRow(rowIndex);
32
+ const taskData = {};
33
+ row.eachCell((cell, colNumber) => {
34
+ var _a;
35
+ const header = headers[colNumber - 1];
36
+ // Check if the cell has a formula(or sharedFormula) and use the formula result
37
+ let cellValue = cell.value;
38
+ if (cellValue && typeof cellValue === 'object' && ('formula' in cellValue || 'sharedFormula' in cellValue)) {
39
+ // Cell contains a formula, use the calculated result if available
40
+ cellValue = (_a = cellValue.result) !== null && _a !== void 0 ? _a : cellValue.value; // Use the result, or fallback to value if result is not calculated
41
+ }
42
+ taskData[header] = cellValue;
43
+ });
44
+ const taskCodeCell = row.getCell(taskCodeColumnIndex);
45
+ let bgColor = '#FFFFFF';
46
+ const fill = taskCodeCell.style.fill;
47
+ if (fill && fill.type === 'pattern' && fill.pattern === 'solid') {
48
+ const fgColor = fill.fgColor;
49
+ if (fgColor && fgColor.argb) {
50
+ // ARGB is a color in the format AARRGGBB, remove the alpha channel (first two characters)
51
+ bgColor = `#${fgColor.argb.slice(2)}`;
52
+ }
53
+ }
54
+ const task = {
55
+ code: taskData['작업코드'],
56
+ title: taskData['작업명'],
57
+ type: taskData['세부공종'],
58
+ duration: taskData['기간'],
59
+ startDate: taskData['시작일'],
60
+ dependsOn: taskData['선행작업코드'],
61
+ progress: taskData['진척율'],
62
+ tags: taskData['Tags'] ? taskData['Tags'].split(',') : [],
63
+ resources: taskData['Resources'] ? JSON.parse(taskData['Resources']) : [],
64
+ style: bgColor,
65
+ children: []
66
+ };
67
+ if (task.code && task.type) {
68
+ tasks.push(task);
69
+ }
70
+ }
71
+ // 5. 변환된 데이터를 importTasks 함수로 전달합니다.
72
+ await (0, import_task_1.importTasks)(project, tasks, context);
73
+ }
74
+ exports.parseExcelAndImportTasks = parseExcelAndImportTasks;
75
+ //# sourceMappingURL=parse-excel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-excel.js","sourceRoot":"","sources":["../../server/controllers/parse-excel.ts"],"names":[],"mappings":"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","sourcesContent":["import ExcelJS from 'exceljs'\nimport { RawTask } from './types'\nimport { Project } from '../service/project/project'\nimport { importTasks } from './import-task'\n\nexport async function parseExcelAndImportTasks(buffer: Buffer, project: Project, context: ResolverContext) {\n // 1. 엑셀 파일을 읽어들입니다.\n const workbook = new ExcelJS.Workbook()\n await workbook.xlsx.load(buffer)\n\n // 2. 첫 번째 워크시트를 가져옵니다.\n const worksheet = workbook.getWorksheet(1) // Index or sheet name can be used\n\n // 3. 첫 번째 row를 header로 사용합니다.\n const headers: string[] = []\n let taskCodeColumnIndex = -1\n\n const headerRow = worksheet.getRow(1)\n headerRow.eachCell((cell, colNumber) => {\n const headerText = cell.text.toString()\n headers[colNumber - 1] = headerText // Store headers in an array\n\n if (headerText === '작업코드') {\n taskCodeColumnIndex = colNumber // Store the column index for \"작업코드\"\n }\n })\n\n if (taskCodeColumnIndex === -1) {\n throw new Error('작업코드 column not found')\n }\n\n // 4. 엑셀 데이터를 RawTask 형식으로 변환합니다.\n const tasks: RawTask[] = []\n\n // Start processing from the second row onward to skip the header\n for (let rowIndex = 2; rowIndex <= worksheet.rowCount; rowIndex++) {\n const row = worksheet.getRow(rowIndex)\n const taskData: any = {}\n\n row.eachCell((cell, colNumber) => {\n const header = headers[colNumber - 1]\n\n // Check if the cell has a formula(or sharedFormula) and use the formula result\n let cellValue: any = cell.value\n if (cellValue && typeof cellValue === 'object' && ('formula' in cellValue || 'sharedFormula' in cellValue)) {\n // Cell contains a formula, use the calculated result if available\n cellValue = cellValue.result ?? cellValue.value // Use the result, or fallback to value if result is not calculated\n }\n\n taskData[header] = cellValue\n })\n\n const taskCodeCell = row.getCell(taskCodeColumnIndex)\n let bgColor = '#FFFFFF'\n const fill = taskCodeCell.style.fill\n\n if (fill && fill.type === 'pattern' && fill.pattern === 'solid') {\n const fgColor = fill.fgColor\n if (fgColor && fgColor.argb) {\n // ARGB is a color in the format AARRGGBB, remove the alpha channel (first two characters)\n bgColor = `#${fgColor.argb.slice(2)}`\n }\n }\n\n const task: RawTask = {\n code: taskData['작업코드'],\n title: taskData['작업명'],\n type: taskData['세부공종'],\n duration: taskData['기간'],\n startDate: taskData['시작일'],\n dependsOn: taskData['선행작업코드'],\n progress: taskData['진척율'],\n tags: taskData['Tags'] ? taskData['Tags'].split(',') : [],\n resources: taskData['Resources'] ? JSON.parse(taskData['Resources']) : [],\n style: bgColor,\n children: []\n }\n\n if (task.code && task.type) {\n tasks.push(task)\n }\n }\n\n // 5. 변환된 데이터를 importTasks 함수로 전달합니다.\n await importTasks(project, tasks, context)\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import { TaskType } from '../service/task/task';
2
+ export interface RawResource {
3
+ type: string;
4
+ allocated: number;
5
+ }
6
+ export interface RawTask {
7
+ code: string;
8
+ title: string;
9
+ type?: TaskType;
10
+ duration?: number;
11
+ startDate?: Date | string;
12
+ dependsOn?: string;
13
+ progress?: number;
14
+ tags?: string[];
15
+ style?: string;
16
+ resources?: RawResource[];
17
+ children?: RawTask[];
18
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../server/controllers/types.ts"],"names":[],"mappings":"","sourcesContent":["import { Task, TaskType } from '../service/task/task'\n\nexport interface RawResource {\n type: string\n allocated: number\n}\n\nexport interface RawTask {\n code: string\n title: string\n type?: TaskType\n duration?: number\n startDate?: Date | string\n dependsOn?: string\n progress?: number\n tags?: string[]\n style?: string\n resources?: RawResource[]\n children?: RawTask[]\n}\n"]}
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const content_disposition_1 = tslib_1.__importDefault(require("content-disposition"));
5
- const project_to_excel_1 = require("./controllers/project-to-excel");
5
+ const export_tasks_1 = require("./controllers/export-tasks");
6
6
  // const debug = require('debug')('dssp:project:routes')
7
7
  process.on('bootstrap-module-global-public-route', (app, globalPublicRouter) => {
8
8
  /*
@@ -84,7 +84,7 @@ process.on('bootstrap-module-global-public-route', (app, globalPublicRouter) =>
84
84
  ];
85
85
  context.type = 'application/xlsx';
86
86
  context.set('Content-Disposition', (0, content_disposition_1.default)(`project.xlsx`));
87
- context.body = await (0, project_to_excel_1.generateExcel)(tasks);
87
+ context.body = await (0, export_tasks_1.generateExcel)(tasks);
88
88
  });
89
89
  });
90
90
  process.on('bootstrap-module-global-private-route', (app, globalPrivateRouter) => {
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../server/routes.ts"],"names":[],"mappings":"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","sourcesContent":["import contentDisposition from 'content-disposition'\n\nimport { Task, generateExcel } from './controllers/project-to-excel'\n\n// const debug = require('debug')('dssp:project:routes')\n\nprocess.on('bootstrap-module-global-public-route' as any, (app, globalPublicRouter) => {\n /*\n * can add global public routes to application (auth not required, tenancy not required)\n *\n * ex) routes.get('/path', async(context, next) => {})\n * ex) routes.post('/path', async(context, next) => {})\n */\n globalPublicRouter.get('/export-project', async (context, next) => {\n const tasks: Task[] = [\n {\n name: '1 Task',\n startDate: new Date('2024-03-01'),\n endDate: new Date('2024-03-05'),\n subtasks: [\n {\n name: '1.1 Subtask',\n startDate: new Date('2024-03-02'),\n endDate: new Date('2024-03-03'),\n subtasks: [\n {\n name: '1.1.1 Subtask',\n startDate: new Date('2024-03-02'),\n endDate: new Date('2024-03-02')\n },\n {\n name: '1.1.2 Subtask',\n startDate: new Date('2024-03-02'),\n endDate: new Date('2024-03-03')\n },\n {\n name: '1.1.2 Subtask',\n startDate: new Date('2024-03-03'),\n endDate: new Date('2024-03-03')\n },\n {\n name: '1.1.2 Subtask',\n startDate: new Date('2024-03-03'),\n endDate: new Date('2024-03-03')\n }\n ]\n },\n {\n name: '1.2 Subtask',\n startDate: new Date('2024-03-04'),\n endDate: new Date('2024-03-05')\n }\n ]\n },\n {\n name: '2 Task',\n startDate: new Date('2024-03-06'),\n endDate: new Date('2024-03-10'),\n subtasks: [\n {\n name: '2.1 Subtask',\n startDate: new Date('2024-03-06'),\n endDate: new Date('2024-03-07')\n },\n {\n name: '2.2 Subtask',\n startDate: new Date('2024-03-08'),\n endDate: new Date('2024-03-10'),\n subtasks: [\n {\n name: '2.2.1 Subtask',\n startDate: new Date('2024-03-08'),\n endDate: new Date('2024-03-09')\n },\n {\n name: '2.2.2 Subtask',\n startDate: new Date('2024-03-10'),\n endDate: new Date('2024-03-10')\n }\n ]\n }\n ]\n }\n ]\n\n context.type = 'application/xlsx'\n context.set('Content-Disposition', contentDisposition(`project.xlsx`))\n context.body = await generateExcel(tasks)\n })\n})\n\nprocess.on('bootstrap-module-global-private-route' as any, (app, globalPrivateRouter) => {\n /*\n * can add global private routes to application (auth required, tenancy not required)\n */\n})\n\nprocess.on('bootstrap-module-domain-public-route' as any, (app, domainPublicRouter) => {\n /*\n * can add domain public routes to application (auth not required, tenancy required)\n */\n})\n\nprocess.on('bootstrap-module-domain-private-route' as any, (app, domainPrivateRouter) => {\n /*\n * can add domain private routes to application (auth required, tenancy required)\n */\n})\n"]}
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../server/routes.ts"],"names":[],"mappings":";;;AAAA,sFAAoD;AAEpD,6DAAgE;AAEhE,wDAAwD;AAExD,OAAO,CAAC,EAAE,CAAC,sCAA6C,EAAE,CAAC,GAAG,EAAE,kBAAkB,EAAE,EAAE;IACpF;;;;;OAKG;IACH,kBAAkB,CAAC,GAAG,CAAC,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QAChE,MAAM,KAAK,GAAW;YACpB;gBACE,IAAI,EAAE,QAAQ;gBACd,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gBACjC,OAAO,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gBAC/B,QAAQ,EAAE;oBACR;wBACE,IAAI,EAAE,aAAa;wBACnB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;wBACjC,OAAO,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;wBAC/B,QAAQ,EAAE;4BACR;gCACE,IAAI,EAAE,eAAe;gCACrB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gCACjC,OAAO,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;6BAChC;4BACD;gCACE,IAAI,EAAE,eAAe;gCACrB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gCACjC,OAAO,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;6BAChC;4BACD;gCACE,IAAI,EAAE,eAAe;gCACrB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gCACjC,OAAO,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;6BAChC;4BACD;gCACE,IAAI,EAAE,eAAe;gCACrB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gCACjC,OAAO,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;6BAChC;yBACF;qBACF;oBACD;wBACE,IAAI,EAAE,aAAa;wBACnB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;wBACjC,OAAO,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;qBAChC;iBACF;aACF;YACD;gBACE,IAAI,EAAE,QAAQ;gBACd,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gBACjC,OAAO,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gBAC/B,QAAQ,EAAE;oBACR;wBACE,IAAI,EAAE,aAAa;wBACnB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;wBACjC,OAAO,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;qBAChC;oBACD;wBACE,IAAI,EAAE,aAAa;wBACnB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;wBACjC,OAAO,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;wBAC/B,QAAQ,EAAE;4BACR;gCACE,IAAI,EAAE,eAAe;gCACrB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gCACjC,OAAO,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;6BAChC;4BACD;gCACE,IAAI,EAAE,eAAe;gCACrB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gCACjC,OAAO,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;6BAChC;yBACF;qBACF;iBACF;aACF;SACF,CAAA;QAED,OAAO,CAAC,IAAI,GAAG,kBAAkB,CAAA;QACjC,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,IAAA,6BAAkB,EAAC,cAAc,CAAC,CAAC,CAAA;QACtE,OAAO,CAAC,IAAI,GAAG,MAAM,IAAA,4BAAa,EAAC,KAAK,CAAC,CAAA;IAC3C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,OAAO,CAAC,EAAE,CAAC,uCAA8C,EAAE,CAAC,GAAG,EAAE,mBAAmB,EAAE,EAAE;IACtF;;OAEG;AACL,CAAC,CAAC,CAAA;AAEF,OAAO,CAAC,EAAE,CAAC,sCAA6C,EAAE,CAAC,GAAG,EAAE,kBAAkB,EAAE,EAAE;IACpF;;OAEG;AACL,CAAC,CAAC,CAAA;AAEF,OAAO,CAAC,EAAE,CAAC,uCAA8C,EAAE,CAAC,GAAG,EAAE,mBAAmB,EAAE,EAAE;IACtF;;OAEG;AACL,CAAC,CAAC,CAAA","sourcesContent":["import contentDisposition from 'content-disposition'\n\nimport { Task, generateExcel } from './controllers/export-tasks'\n\n// const debug = require('debug')('dssp:project:routes')\n\nprocess.on('bootstrap-module-global-public-route' as any, (app, globalPublicRouter) => {\n /*\n * can add global public routes to application (auth not required, tenancy not required)\n *\n * ex) routes.get('/path', async(context, next) => {})\n * ex) routes.post('/path', async(context, next) => {})\n */\n globalPublicRouter.get('/export-project', async (context, next) => {\n const tasks: Task[] = [\n {\n name: '1 Task',\n startDate: new Date('2024-03-01'),\n endDate: new Date('2024-03-05'),\n subtasks: [\n {\n name: '1.1 Subtask',\n startDate: new Date('2024-03-02'),\n endDate: new Date('2024-03-03'),\n subtasks: [\n {\n name: '1.1.1 Subtask',\n startDate: new Date('2024-03-02'),\n endDate: new Date('2024-03-02')\n },\n {\n name: '1.1.2 Subtask',\n startDate: new Date('2024-03-02'),\n endDate: new Date('2024-03-03')\n },\n {\n name: '1.1.2 Subtask',\n startDate: new Date('2024-03-03'),\n endDate: new Date('2024-03-03')\n },\n {\n name: '1.1.2 Subtask',\n startDate: new Date('2024-03-03'),\n endDate: new Date('2024-03-03')\n }\n ]\n },\n {\n name: '1.2 Subtask',\n startDate: new Date('2024-03-04'),\n endDate: new Date('2024-03-05')\n }\n ]\n },\n {\n name: '2 Task',\n startDate: new Date('2024-03-06'),\n endDate: new Date('2024-03-10'),\n subtasks: [\n {\n name: '2.1 Subtask',\n startDate: new Date('2024-03-06'),\n endDate: new Date('2024-03-07')\n },\n {\n name: '2.2 Subtask',\n startDate: new Date('2024-03-08'),\n endDate: new Date('2024-03-10'),\n subtasks: [\n {\n name: '2.2.1 Subtask',\n startDate: new Date('2024-03-08'),\n endDate: new Date('2024-03-09')\n },\n {\n name: '2.2.2 Subtask',\n startDate: new Date('2024-03-10'),\n endDate: new Date('2024-03-10')\n }\n ]\n }\n ]\n }\n ]\n\n context.type = 'application/xlsx'\n context.set('Content-Disposition', contentDisposition(`project.xlsx`))\n context.body = await generateExcel(tasks)\n })\n})\n\nprocess.on('bootstrap-module-global-private-route' as any, (app, globalPrivateRouter) => {\n /*\n * can add global private routes to application (auth required, tenancy not required)\n */\n})\n\nprocess.on('bootstrap-module-domain-public-route' as any, (app, domainPublicRouter) => {\n /*\n * can add domain public routes to application (auth not required, tenancy required)\n */\n})\n\nprocess.on('bootstrap-module-domain-private-route' as any, (app, domainPrivateRouter) => {\n /*\n * can add domain private routes to application (auth required, tenancy required)\n */\n})\n"]}
@@ -1,3 +1,4 @@
1
+ import { Attachment } from '@things-factory/attachment-base';
1
2
  import { Project } from './project';
2
3
  import { NewProject, ProjectPatch, UploadProjectScheduleTable } from './project-type';
3
4
  export declare class ProjectMutation {
@@ -7,4 +8,4 @@ export declare class ProjectMutation {
7
8
  uploadProjectScheduleTable(param: UploadProjectScheduleTable, context: ResolverContext): Promise<boolean>;
8
9
  deleteProject(id: string, context: ResolverContext): Promise<boolean>;
9
10
  }
10
- export declare function createAttachmentAfterDelete(context: ResolverContext, file: any, refBy: any, refType: any): Promise<any>;
11
+ export declare function createAttachmentAfterDelete(context: ResolverContext, file: any, refBy: any, refType: any): Promise<Attachment>;
@@ -4,16 +4,18 @@ exports.createAttachmentAfterDelete = exports.ProjectMutation = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const type_graphql_1 = require("type-graphql");
6
6
  const typeorm_1 = require("typeorm");
7
+ const shell_1 = require("@things-factory/shell");
7
8
  const attachment_base_1 = require("@things-factory/attachment-base");
8
9
  const project_1 = require("./project");
9
10
  const project_type_1 = require("./project-type");
10
11
  const building_complex_1 = require("@dssp/building-complex");
11
12
  const headless_pdf_to_image_1 = require("@things-factory/board-service/dist-server/controllers/headless-pdf-to-image");
13
+ const parse_excel_1 = require("../../controllers/parse-excel");
12
14
  let ProjectMutation = class ProjectMutation {
13
15
  async createProject(project, context) {
14
16
  const { domain, user, tx } = context.state;
15
- const projectRepo = tx.getRepository(project_1.Project);
16
- const buildingComplexRepo = tx.getRepository(building_complex_1.BuildingComplex);
17
+ const projectRepo = (0, shell_1.getRepository)(project_1.Project, tx);
18
+ const buildingComplexRepo = (0, shell_1.getRepository)(building_complex_1.BuildingComplex, tx);
17
19
  const newBuildingComplex = await buildingComplexRepo.save({
18
20
  domain,
19
21
  creator: user,
@@ -31,10 +33,10 @@ let ProjectMutation = class ProjectMutation {
31
33
  async updateProject(project, context) {
32
34
  var _a;
33
35
  const { user, tx } = context.state;
34
- const projectRepo = tx.getRepository(project_1.Project);
35
- const buildingComplexRepo = tx.getRepository(building_complex_1.BuildingComplex);
36
- const buildingRepo = tx.getRepository(building_complex_1.Building);
37
- const buildingLevelRepo = tx.getRepository(building_complex_1.BuildingLevel);
36
+ const projectRepo = (0, shell_1.getRepository)(project_1.Project, tx);
37
+ const buildingComplexRepo = (0, shell_1.getRepository)(building_complex_1.BuildingComplex, tx);
38
+ const buildingRepo = (0, shell_1.getRepository)(building_complex_1.Building, tx);
39
+ const buildingLevelRepo = (0, shell_1.getRepository)(building_complex_1.BuildingLevel, tx);
38
40
  const buildingComplex = project.buildingComplex;
39
41
  const buildings = ((_a = project.buildingComplex) === null || _a === void 0 ? void 0 : _a.buildings) || [];
40
42
  // 1. 프로젝트 수정
@@ -79,10 +81,10 @@ let ProjectMutation = class ProjectMutation {
79
81
  async updateProjectPlan(project, context) {
80
82
  var _a;
81
83
  const { user, tx, domain } = context.state;
82
- const projectRepo = tx.getRepository(project_1.Project);
83
- const buildingComplexRepo = tx.getRepository(building_complex_1.BuildingComplex);
84
- const buildingRepo = tx.getRepository(building_complex_1.Building);
85
- const buildingLevelRepo = tx.getRepository(building_complex_1.BuildingLevel);
84
+ const projectRepo = (0, shell_1.getRepository)(project_1.Project, tx);
85
+ const buildingComplexRepo = (0, shell_1.getRepository)(building_complex_1.BuildingComplex, tx);
86
+ const buildingRepo = (0, shell_1.getRepository)(building_complex_1.Building, tx);
87
+ const buildingLevelRepo = (0, shell_1.getRepository)(building_complex_1.BuildingLevel, tx);
86
88
  const buildingComplex = project.buildingComplex;
87
89
  const buildings = ((_a = project.buildingComplex) === null || _a === void 0 ? void 0 : _a.buildings) || [];
88
90
  // 1. 프로젝트 수정 시간 업데이트
@@ -98,7 +100,7 @@ let ProjectMutation = class ProjectMutation {
98
100
  // 첨부된 PDF가 있으면 PDF 파일대로 썸네일 생성
99
101
  if (mainDrawingAttatchment) {
100
102
  const mainDrawingUpload = await buildingLevel.mainDrawingUpload;
101
- const pdfPath = `/${attachment_base_1.ATTACHMENT_PATH}/${mainDrawingAttatchment.path}`;
103
+ const pdfPath = `/${attachment_base_1.ATTACHMENT_PATH}/${mainDrawingAttatchment.path}`; // TODO ATTACHMENT_PATH 제거, mainDrawingAttachment.fullpath 로 해도 될 것 같은데...
102
104
  const fileName = mainDrawingUpload.filename.replace('.pdf', '');
103
105
  const pngFile = await (0, headless_pdf_to_image_1.pdfToImage)({ pdfPath, fileName });
104
106
  await createAttachmentAfterDelete(context, pngFile, buildingLevel.id, building_complex_1.BuildingLevel.name + '_mainDrawing_image');
@@ -134,15 +136,44 @@ let ProjectMutation = class ProjectMutation {
134
136
  return projectResult;
135
137
  }
136
138
  async uploadProjectScheduleTable(param, context) {
137
- const { user, tx } = context.state;
139
+ var _a, e_1, _b, _c;
140
+ const { domain, user, tx } = context.state;
138
141
  const { projectId, scheduleTable } = param;
139
- // 프로젝트 공정표 파일 업로드
140
- await createAttachmentAfterDelete(context, scheduleTable, projectId, project_1.Project.name + '_schedule_table');
142
+ const projectRepo = (0, shell_1.getRepository)(project_1.Project, tx);
143
+ const project = await projectRepo.findOne({
144
+ where: { domain: { id: domain.id }, id: projectId }
145
+ });
146
+ const { createReadStream, filename, mimetype } = await scheduleTable;
147
+ const stream = createReadStream();
148
+ const chunks = [];
149
+ try {
150
+ for (var _d = true, stream_1 = tslib_1.__asyncValues(stream), stream_1_1; stream_1_1 = await stream_1.next(), _a = stream_1_1.done, !_a;) {
151
+ _c = stream_1_1.value;
152
+ _d = false;
153
+ try {
154
+ const chunk = _c;
155
+ chunks.push(chunk);
156
+ }
157
+ finally {
158
+ _d = true;
159
+ }
160
+ }
161
+ }
162
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
163
+ finally {
164
+ try {
165
+ if (!_d && !_a && (_b = stream_1.return)) await _b.call(stream_1);
166
+ }
167
+ finally { if (e_1) throw e_1.error; }
168
+ }
169
+ const buffer = Buffer.concat(chunks);
170
+ await (0, parse_excel_1.parseExcelAndImportTasks)(buffer, project, context);
171
+ // await parseExcelAndImportTasks(attachment.fullpath, project, context)
141
172
  return true;
142
173
  }
143
174
  async deleteProject(id, context) {
144
175
  const { domain, tx } = context.state;
145
- await tx.getRepository(project_1.Project).delete({ domain: { id: domain.id }, id });
176
+ await (0, shell_1.getRepository)(project_1.Project, tx).delete({ domain: { id: domain.id }, id });
146
177
  await (0, attachment_base_1.deleteAttachmentsByRef)(null, { refBys: [id] }, context);
147
178
  return true;
148
179
  }
@@ -197,17 +228,14 @@ ProjectMutation = tslib_1.__decorate([
197
228
  ], ProjectMutation);
198
229
  exports.ProjectMutation = ProjectMutation;
199
230
  async function createAttachmentAfterDelete(context, file, refBy, refType) {
200
- let result = null;
201
- // undefined = 기존 파일 그대로
202
- if (file === undefined)
203
- return result;
231
+ if (file === undefined) {
232
+ return null;
233
+ }
234
+ const { tx } = context.state;
204
235
  // 기존 첨부 파일이 있으면 삭제
205
236
  await (0, attachment_base_1.deleteAttachmentsByRef)(null, { refBys: [refBy], refType }, context);
206
- // 파일이 있으면 생성 (null 들어올 경우 delete까지만)
207
- if (file) {
208
- result = await (0, attachment_base_1.createAttachment)(null, { attachment: { file, refType, refBy } }, context);
209
- }
210
- return result;
237
+ let result = await (0, attachment_base_1.createAttachment)(null, { attachment: { file, refType, refBy } }, context);
238
+ return await (0, shell_1.getRepository)(attachment_base_1.Attachment, tx).findOne({ where: { id: result.id } });
211
239
  }
212
240
  exports.createAttachmentAfterDelete = createAttachmentAfterDelete;
213
241
  //# sourceMappingURL=project-mutation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"project-mutation.js","sourceRoot":"","sources":["../../../server/service/project/project-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'\nimport { In } from 'typeorm'\nimport { createAttachment, deleteAttachmentsByRef, ATTACHMENT_PATH } from '@things-factory/attachment-base'\nimport { Project, ProjectState } from './project'\nimport { NewProject, ProjectPatch, UploadProjectScheduleTable } from './project-type'\nimport { BuildingComplex, Building, BuildingLevel } from '@dssp/building-complex'\nimport { pdfToImage } from '@things-factory/board-service/dist-server/controllers/headless-pdf-to-image'\n\n@Resolver(Project)\nexport class ProjectMutation {\n @Directive('@transaction')\n @Mutation(returns => Project, { description: '프로젝트 생성' })\n async createProject(@Arg('project') project: NewProject, @Ctx() context: ResolverContext): Promise<Project> {\n const { domain, user, tx } = context.state\n const projectRepo = tx.getRepository(Project)\n const buildingComplexRepo = tx.getRepository(BuildingComplex)\n\n const newBuildingComplex = await buildingComplexRepo.save({\n domain,\n creator: user,\n updater: user\n })\n\n const result = await projectRepo.save({\n name: project.name,\n buildingComplex: newBuildingComplex,\n domain,\n creator: user,\n updater: user\n })\n\n return result\n }\n\n @Directive('@transaction')\n @Mutation(returns => Project, { description: '프로젝트 업데이트' })\n async updateProject(@Arg('project') project: ProjectPatch, @Ctx() context: ResolverContext): Promise<Project> {\n const { user, tx } = context.state\n const projectRepo = tx.getRepository(Project)\n const buildingComplexRepo = tx.getRepository(BuildingComplex)\n const buildingRepo = tx.getRepository(Building)\n const buildingLevelRepo = tx.getRepository(BuildingLevel)\n\n const buildingComplex = project.buildingComplex\n const buildings = project.buildingComplex?.buildings || []\n\n // 1. 프로젝트 수정\n const projectState = project.totalProgress == 100 ? ProjectState.COMPLETED : ProjectState.ONGOING\n const projectResult = await projectRepo.save({ ...project, state: projectState, updater: user })\n\n // 2. 단지 정보 수정\n await buildingComplexRepo.save({ ...buildingComplex, updater: user })\n\n // 2-1. 프로젝트 메인 이미지 첨부파일 나머지 삭제 후 저장\n await createAttachmentAfterDelete(context, project?.mainPhotoUpload, project.id, Project.name)\n\n // 2-2. 단지 BIM 이미지 첨부파일 나머지 삭제 후 저장\n await createAttachmentAfterDelete(context, buildingComplex?.drawingUpload, buildingComplex.id, BuildingComplex.name + '_bim')\n\n // 3. 동의 층 정보가 바뀌었으면 층 초기화 후 다시 생성\n const originBuilding = await buildingRepo.findBy({ buildingComplex: { id: buildingComplex.id } }) // 이전 동 정보 가져오기\n const afterBuilding = buildings.reduce((acc, building) => ({ ...acc, [building.name]: building.floorCount }), {}) // 비교용으로 수정된 동 정보 데이터 파싱\n const isBuidlingChanged = originBuilding.some(building => afterBuilding[building.name] !== building.floorCount) // 층 개수가 달라진 동이 있는지 확인\n\n // 동의 층 개수가 달라지면 모든 층의 데이터 제거 후 생성\n if (isBuidlingChanged || originBuilding.length !== buildings.length) {\n // 3-1. 기존 동/층 첨부파일 및 데이터 제거\n const buildingIds = originBuilding.map((building: Building) => building.id)\n const buildingLevels = await buildingLevelRepo.findBy({ building: { id: In(buildingIds) } })\n const buildingLevelIds = buildingLevels.map((buildingLevel: BuildingLevel) => buildingLevel.id)\n\n await buildingLevelRepo.softDelete({ building: { id: In(buildingIds) } })\n await buildingRepo.softDelete({ id: In(buildingIds) })\n await deleteAttachmentsByRef(null, { refBys: [...buildingIds, ...buildingLevelIds] }, context)\n\n // 3-2. 단지 내 동 정보들 생성\n for (let buildingKey in buildings) {\n const building = buildings[buildingKey]\n const newBuilding = await buildingRepo.save({\n buildingComplex: buildingComplex,\n name: building.name,\n floorCount: building.floorCount,\n creator: user\n })\n\n // 3-3. 동별로 for문 돌면서 층 데이터 개수대로 생성\n for (let i = 1; i <= building.floorCount; i++) {\n await buildingLevelRepo.save({ building: newBuilding, floor: i, creator: user })\n }\n }\n }\n\n return projectResult\n }\n\n @Directive('@transaction')\n @Mutation(returns => Project, { description: '프로젝트 도면 업데이트' })\n async updateProjectPlan(@Arg('project') project: ProjectPatch, @Ctx() context: ResolverContext): Promise<Project> {\n const { user, tx, domain } = context.state\n const projectRepo = tx.getRepository(Project)\n const buildingComplexRepo = tx.getRepository(BuildingComplex)\n const buildingRepo = tx.getRepository(Building)\n const buildingLevelRepo = tx.getRepository(BuildingLevel)\n const buildingComplex = project.buildingComplex\n const buildings = project.buildingComplex?.buildings || []\n\n // 1. 프로젝트 수정 시간 업데이트\n const projectResult = await projectRepo.save({ ...project, updater: user })\n\n // 2. 단지 축척 정보 수정\n await buildingComplexRepo.save({ ...buildingComplex, updater: user })\n\n for (let buildingKey in buildings) {\n const building = buildings[buildingKey]\n\n for (let buildingLevelKey in building.buildingLevels) {\n const buildingLevel = building.buildingLevels[buildingLevelKey]\n\n // 3. 층별 도면 이미지 저장\n const mainDrawingAttatchment = await createAttachmentAfterDelete(\n context,\n buildingLevel.mainDrawingUpload,\n buildingLevel.id,\n BuildingLevel.name + '_mainDrawing'\n )\n // 첨부된 PDF가 있으면 PDF 파일대로 썸네일 생성\n if (mainDrawingAttatchment) {\n const mainDrawingUpload = await buildingLevel.mainDrawingUpload\n const pdfPath = `/${ATTACHMENT_PATH}/${mainDrawingAttatchment.path}`\n const fileName = mainDrawingUpload.filename.replace('.pdf', '')\n const pngFile = await pdfToImage({ pdfPath, fileName })\n await createAttachmentAfterDelete(context, pngFile, buildingLevel.id, BuildingLevel.name + '_mainDrawing_image')\n\n const pngThumbnailFile = await pdfToImage({ pdfPath, fileName, defaultViewport: { width: 300, height: 200 } })\n await createAttachmentAfterDelete(\n context,\n pngThumbnailFile,\n buildingLevel.id,\n BuildingLevel.name + '_mainDrawing_thumbnail'\n )\n }\n\n // 3-1. 입면도 파일 저장\n const elevationDrawingAttatchment = await createAttachmentAfterDelete(\n context,\n buildingLevel.elevationDrawingUpload,\n buildingLevel.id,\n BuildingLevel.name + '_elevationDrawing'\n )\n if (elevationDrawingAttatchment) {\n const elevationDrawingUpload = await buildingLevel.elevationDrawingUpload\n const pdfPath = `/${ATTACHMENT_PATH}/${elevationDrawingAttatchment.path}`\n const fileName = elevationDrawingUpload.filename.replace('.pdf', '')\n const pngThumbnailFile = await pdfToImage({ pdfPath, fileName, defaultViewport: { width: 300, height: 200 } })\n await createAttachmentAfterDelete(\n context,\n pngThumbnailFile,\n buildingLevel.id,\n BuildingLevel.name + '_elevationDrawing_thumbnail'\n )\n }\n\n // 3-2. 철근배분도 파일 저장\n const rebarDistributionDrawingAttatchment = await createAttachmentAfterDelete(\n context,\n buildingLevel.rebarDistributionDrawingUpload,\n buildingLevel.id,\n BuildingLevel.name + '_rebarDistributionDrawing'\n )\n if (rebarDistributionDrawingAttatchment) {\n const rebarDistributionDrawingUpload = await buildingLevel.rebarDistributionDrawingUpload\n const pdfPath = `/${ATTACHMENT_PATH}/${rebarDistributionDrawingAttatchment.path}`\n const fileName = rebarDistributionDrawingUpload.filename.replace('.pdf', '')\n const pngThumbnailFile = await pdfToImage({ pdfPath, fileName, defaultViewport: { width: 300, height: 200 } })\n await createAttachmentAfterDelete(\n context,\n pngThumbnailFile,\n buildingLevel.id,\n BuildingLevel.name + '_rebarDistributionDrawing_thumbnail'\n )\n }\n\n // 3-3. 층 업데이트 시간 갱신\n await buildingLevelRepo.save({ ...buildingLevel, updater: user })\n }\n\n // 4. 동별 도면 이미지 저장\n await createAttachmentAfterDelete(context, building?.drawingUpload, building.id, Building.name)\n\n // 4-1. 동 업데이트 시간 갱신\n await buildingRepo.save({ ...building, updater: user })\n }\n\n return projectResult\n }\n\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: '프로젝트 공정표 업로드' })\n async uploadProjectScheduleTable(\n @Arg('param') param: UploadProjectScheduleTable,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { user, tx } = context.state\n const { projectId, scheduleTable } = param\n\n // 프로젝트 공정표 파일 업로드\n await createAttachmentAfterDelete(context, scheduleTable, projectId, Project.name + '_schedule_table')\n\n return true\n }\n\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: 'To delete Project' })\n async deleteProject(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {\n const { domain, tx } = context.state\n\n await tx.getRepository(Project).delete({ domain: { id: domain.id }, id })\n await deleteAttachmentsByRef(null, { refBys: [id] }, context)\n\n return true\n }\n}\n\nexport async function createAttachmentAfterDelete(context: ResolverContext, file: any, refBy: any, refType: any) {\n let result = null\n\n // undefined = 기존 파일 그대로\n if (file === undefined) return result\n\n // 기존 첨부 파일이 있으면 삭제\n await deleteAttachmentsByRef(null, { refBys: [refBy], refType }, context)\n\n // 파일이 있으면 생성 (null로 들어올 경우 delete까지만)\n if (file) {\n result = await createAttachment(null, { attachment: { file, refType, refBy } }, context)\n }\n\n return result\n}\n"]}
1
+ {"version":3,"file":"project-mutation.js","sourceRoot":"","sources":["../../../server/service/project/project-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'\nimport { In } from 'typeorm'\nimport { getRepository } from '@things-factory/shell'\nimport { Attachment, createAttachment, deleteAttachmentsByRef, ATTACHMENT_PATH } from '@things-factory/attachment-base'\nimport { Project, ProjectState } from './project'\nimport { NewProject, ProjectPatch, UploadProjectScheduleTable } from './project-type'\nimport { BuildingComplex, Building, BuildingLevel } from '@dssp/building-complex'\nimport { pdfToImage } from '@things-factory/board-service/dist-server/controllers/headless-pdf-to-image'\n\nimport { parseExcelAndImportTasks } from '../../controllers/parse-excel'\n@Resolver(Project)\nexport class ProjectMutation {\n @Directive('@transaction')\n @Mutation(returns => Project, { description: '프로젝트 생성' })\n async createProject(@Arg('project') project: NewProject, @Ctx() context: ResolverContext): Promise<Project> {\n const { domain, user, tx } = context.state\n const projectRepo = getRepository(Project, tx)\n const buildingComplexRepo = getRepository(BuildingComplex, tx)\n\n const newBuildingComplex = await buildingComplexRepo.save({\n domain,\n creator: user,\n updater: user\n })\n\n const result = await projectRepo.save({\n name: project.name,\n buildingComplex: newBuildingComplex,\n domain,\n creator: user,\n updater: user\n })\n\n return result\n }\n\n @Directive('@transaction')\n @Mutation(returns => Project, { description: '프로젝트 업데이트' })\n async updateProject(@Arg('project') project: ProjectPatch, @Ctx() context: ResolverContext): Promise<Project> {\n const { user, tx } = context.state\n const projectRepo = getRepository(Project, tx)\n const buildingComplexRepo = getRepository(BuildingComplex, tx)\n const buildingRepo = getRepository(Building, tx)\n const buildingLevelRepo = getRepository(BuildingLevel, tx)\n\n const buildingComplex = project.buildingComplex\n const buildings = project.buildingComplex?.buildings || []\n\n // 1. 프로젝트 수정\n const projectState = project.totalProgress == 100 ? ProjectState.COMPLETED : ProjectState.ONGOING\n const projectResult = await projectRepo.save({ ...project, state: projectState, updater: user })\n\n // 2. 단지 정보 수정\n await buildingComplexRepo.save({ ...buildingComplex, updater: user })\n\n // 2-1. 프로젝트 메인 이미지 첨부파일 나머지 삭제 후 저장\n await createAttachmentAfterDelete(context, project?.mainPhotoUpload, project.id, Project.name)\n\n // 2-2. 단지 BIM 이미지 첨부파일 나머지 삭제 후 저장\n await createAttachmentAfterDelete(context, buildingComplex?.drawingUpload, buildingComplex.id, BuildingComplex.name + '_bim')\n\n // 3. 동의 층 정보가 바뀌었으면 층 초기화 후 다시 생성\n const originBuilding = await buildingRepo.findBy({ buildingComplex: { id: buildingComplex.id } }) // 이전 동 정보 가져오기\n const afterBuilding = buildings.reduce((acc, building) => ({ ...acc, [building.name]: building.floorCount }), {}) // 비교용으로 수정된 동 정보 데이터 파싱\n const isBuidlingChanged = originBuilding.some(building => afterBuilding[building.name] !== building.floorCount) // 층 개수가 달라진 동이 있는지 확인\n\n // 동의 층 개수가 달라지면 모든 층의 데이터 제거 후 생성\n if (isBuidlingChanged || originBuilding.length !== buildings.length) {\n // 3-1. 기존 동/층 첨부파일 및 데이터 제거\n const buildingIds = originBuilding.map((building: Building) => building.id)\n const buildingLevels = await buildingLevelRepo.findBy({ building: { id: In(buildingIds) } })\n const buildingLevelIds = buildingLevels.map((buildingLevel: BuildingLevel) => buildingLevel.id)\n\n await buildingLevelRepo.softDelete({ building: { id: In(buildingIds) } })\n await buildingRepo.softDelete({ id: In(buildingIds) })\n await deleteAttachmentsByRef(null, { refBys: [...buildingIds, ...buildingLevelIds] }, context)\n\n // 3-2. 단지 내 동 정보들 생성\n for (let buildingKey in buildings) {\n const building = buildings[buildingKey]\n const newBuilding = await buildingRepo.save({\n buildingComplex: buildingComplex,\n name: building.name,\n floorCount: building.floorCount,\n creator: user\n })\n\n // 3-3. 동별로 for문 돌면서 층 데이터 개수대로 생성\n for (let i = 1; i <= building.floorCount; i++) {\n await buildingLevelRepo.save({ building: newBuilding, floor: i, creator: user })\n }\n }\n }\n\n return projectResult\n }\n\n @Directive('@transaction')\n @Mutation(returns => Project, { description: '프로젝트 도면 업데이트' })\n async updateProjectPlan(@Arg('project') project: ProjectPatch, @Ctx() context: ResolverContext): Promise<Project> {\n const { user, tx, domain } = context.state\n const projectRepo = getRepository(Project, tx)\n const buildingComplexRepo = getRepository(BuildingComplex, tx)\n const buildingRepo = getRepository(Building, tx)\n const buildingLevelRepo = getRepository(BuildingLevel, tx)\n const buildingComplex = project.buildingComplex\n const buildings = project.buildingComplex?.buildings || []\n\n // 1. 프로젝트 수정 시간 업데이트\n const projectResult = await projectRepo.save({ ...project, updater: user })\n\n // 2. 단지 축척 정보 수정\n await buildingComplexRepo.save({ ...buildingComplex, updater: user })\n\n for (let buildingKey in buildings) {\n const building = buildings[buildingKey]\n\n for (let buildingLevelKey in building.buildingLevels) {\n const buildingLevel = building.buildingLevels[buildingLevelKey]\n\n // 3. 층별 도면 이미지 저장\n const mainDrawingAttatchment = await createAttachmentAfterDelete(\n context,\n buildingLevel.mainDrawingUpload,\n buildingLevel.id,\n BuildingLevel.name + '_mainDrawing'\n )\n // 첨부된 PDF가 있으면 PDF 파일대로 썸네일 생성\n if (mainDrawingAttatchment) {\n const mainDrawingUpload = await buildingLevel.mainDrawingUpload\n const pdfPath = `/${ATTACHMENT_PATH}/${mainDrawingAttatchment.path}` // TODO ATTACHMENT_PATH 제거, mainDrawingAttachment.fullpath 로 해도 될 것 같은데...\n const fileName = mainDrawingUpload.filename.replace('.pdf', '')\n const pngFile = await pdfToImage({ pdfPath, fileName })\n await createAttachmentAfterDelete(context, pngFile, buildingLevel.id, BuildingLevel.name + '_mainDrawing_image')\n\n const pngThumbnailFile = await pdfToImage({ pdfPath, fileName, defaultViewport: { width: 300, height: 200 } })\n await createAttachmentAfterDelete(\n context,\n pngThumbnailFile,\n buildingLevel.id,\n BuildingLevel.name + '_mainDrawing_thumbnail'\n )\n }\n\n // 3-1. 입면도 파일 저장\n const elevationDrawingAttatchment = await createAttachmentAfterDelete(\n context,\n buildingLevel.elevationDrawingUpload,\n buildingLevel.id,\n BuildingLevel.name + '_elevationDrawing'\n )\n if (elevationDrawingAttatchment) {\n const elevationDrawingUpload = await buildingLevel.elevationDrawingUpload\n const pdfPath = `/${ATTACHMENT_PATH}/${elevationDrawingAttatchment.path}`\n const fileName = elevationDrawingUpload.filename.replace('.pdf', '')\n const pngThumbnailFile = await pdfToImage({ pdfPath, fileName, defaultViewport: { width: 300, height: 200 } })\n await createAttachmentAfterDelete(\n context,\n pngThumbnailFile,\n buildingLevel.id,\n BuildingLevel.name + '_elevationDrawing_thumbnail'\n )\n }\n\n // 3-2. 철근배분도 파일 저장\n const rebarDistributionDrawingAttatchment = await createAttachmentAfterDelete(\n context,\n buildingLevel.rebarDistributionDrawingUpload,\n buildingLevel.id,\n BuildingLevel.name + '_rebarDistributionDrawing'\n )\n if (rebarDistributionDrawingAttatchment) {\n const rebarDistributionDrawingUpload = await buildingLevel.rebarDistributionDrawingUpload\n const pdfPath = `/${ATTACHMENT_PATH}/${rebarDistributionDrawingAttatchment.path}`\n const fileName = rebarDistributionDrawingUpload.filename.replace('.pdf', '')\n const pngThumbnailFile = await pdfToImage({ pdfPath, fileName, defaultViewport: { width: 300, height: 200 } })\n await createAttachmentAfterDelete(\n context,\n pngThumbnailFile,\n buildingLevel.id,\n BuildingLevel.name + '_rebarDistributionDrawing_thumbnail'\n )\n }\n\n // 3-3. 층 업데이트 시간 갱신\n await buildingLevelRepo.save({ ...buildingLevel, updater: user })\n }\n\n // 4. 동별 도면 이미지 저장\n await createAttachmentAfterDelete(context, building?.drawingUpload, building.id, Building.name)\n\n // 4-1. 동 업데이트 시간 갱신\n await buildingRepo.save({ ...building, updater: user })\n }\n\n return projectResult\n }\n\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: '프로젝트 공정표 업로드' })\n async uploadProjectScheduleTable(\n @Arg('param') param: UploadProjectScheduleTable,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, user, tx } = context.state\n const { projectId, scheduleTable } = param\n\n const projectRepo = getRepository(Project, tx)\n const project = await projectRepo.findOne({\n where: { domain: { id: domain.id }, id: projectId }\n })\n\n const { createReadStream, filename, mimetype } = await scheduleTable\n\n const stream = createReadStream()\n\n const chunks = []\n for await (const chunk of stream) {\n chunks.push(chunk)\n }\n\n const buffer = Buffer.concat(chunks)\n\n await parseExcelAndImportTasks(buffer, project, context)\n // await parseExcelAndImportTasks(attachment.fullpath, project, context)\n\n return true\n }\n\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: 'To delete Project' })\n async deleteProject(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {\n const { domain, tx } = context.state\n\n await getRepository(Project, tx).delete({ domain: { id: domain.id }, id })\n await deleteAttachmentsByRef(null, { refBys: [id] }, context)\n\n return true\n }\n}\n\nexport async function createAttachmentAfterDelete(context: ResolverContext, file: any, refBy: any, refType: any) {\n if (file === undefined) {\n return null\n }\n\n const { tx } = context.state\n\n // 기존 첨부 파일이 있으면 삭제\n await deleteAttachmentsByRef(null, { refBys: [refBy], refType }, context)\n\n let result = await createAttachment(null, { attachment: { file, refType, refBy } }, context)\n\n return await getRepository(Attachment, tx).findOne({ where: { id: result.id } })\n}\n"]}
@@ -21,6 +21,7 @@ export declare class Task {
21
21
  projectId?: string;
22
22
  tags?: string[];
23
23
  progress?: number;
24
+ style?: string;
24
25
  taskResources?: TaskResource[];
25
26
  createdAt?: Date;
26
27
  updatedAt?: Date;
@@ -94,6 +94,11 @@ tslib_1.__decorate([
94
94
  (0, type_graphql_1.Field)({ nullable: true }),
95
95
  tslib_1.__metadata("design:type", Number)
96
96
  ], Task.prototype, "progress", void 0);
97
+ tslib_1.__decorate([
98
+ (0, typeorm_1.Column)({ nullable: true, comment: '스타일' }),
99
+ (0, type_graphql_1.Field)({ nullable: true }),
100
+ tslib_1.__metadata("design:type", String)
101
+ ], Task.prototype, "style", void 0);
97
102
  tslib_1.__decorate([
98
103
  (0, typeorm_1.OneToMany)(type => task_resource_1.TaskResource, taskResource => taskResource.task, { cascade: true }),
99
104
  (0, type_graphql_1.Field)(type => [task_resource_1.TaskResource], { nullable: true }),