diff --git a/docker/Dockerfile b/docker/Dockerfile index 009e2b7a6..0885091e3 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,7 +1,7 @@ # docker buildx build --platform=linux/amd64 -f docker/Dockerfile . -t hrms-git.chin.in.th/bma-hrms/hrms-mgt:0.1 # Build Stage -FROM node:latest as build-stage +FROM node:20-alpine as build-stage WORKDIR /app COPY package*.json ./ RUN npm install diff --git a/src/api/09_leave/api.leave.ts b/src/api/09_leave/api.leave.ts index 0ef73073e..120aca326 100644 --- a/src/api/09_leave/api.leave.ts +++ b/src/api/09_leave/api.leave.ts @@ -40,6 +40,10 @@ export default { leaveDeleteReject: (id: string) => `${leave}/admin/delete/reject/${id}`, /**รายงาน */ - leaveReportTimeRecords: () => `${leaveReport}/time-records/officer`, + leaveReportTimeRecords: (type: string) => + `${leaveReport}/time-records/${type}`, + leaveReportTimeLate: (type: string) => `${leaveReport}/late/${type}`, + leaveReportLeaveday: (type: string) => `${leaveReport}/leaveday/${type}`, + leaveReportLeave2: (type: string) => `${leaveReport}/leave2/${type}`, }; diff --git a/src/api/recruiting/api.recruit.ts b/src/api/recruiting/api.recruit.ts index 5b7a0d383..5c9cc69e1 100644 --- a/src/api/recruiting/api.recruit.ts +++ b/src/api/recruiting/api.recruit.ts @@ -41,4 +41,6 @@ export default { exportCandidateList: (id: string) => `${recruit_report}candidate/${id}`, exportPassExamList: (id: string) => `${recruit_report}pass/${id}`, periodRecruitToPlacement: (examId: string) => `${recruit}placement/${examId}`, + + reportRecruit:(type:string)=>`${recruit}${type}` }; diff --git a/src/components/Dialogs/AddPersonal.vue b/src/components/Dialogs/AddPersonal.vue index 72f77fb13..f482c3ae7 100644 --- a/src/components/Dialogs/AddPersonal.vue +++ b/src/components/Dialogs/AddPersonal.vue @@ -134,7 +134,7 @@ async function getSearch() { }; await http .post( - config.API.orgSearchPersonal(), + config.API.orgSearchPersonal()+`?page=${pagination.value.page}&pageSize=${pagination.value.rowsPerPage}`, body ) .then((res) => { diff --git a/src/modules/03_recruiting/router.ts b/src/modules/03_recruiting/router.ts index ca802c4ad..eacfc8606 100644 --- a/src/modules/03_recruiting/router.ts +++ b/src/modules/03_recruiting/router.ts @@ -42,6 +42,8 @@ const Payment = () => import("@/modules/03_recruiting/views/02_qualify/Payment.vue"); const EditorWeb = () => import("@/modules/03_recruiting/views/03_editor/index.vue"); +const CompeteReport = () => + import("@/modules/03_recruiting/views/01_compete/CompeteReport.vue"); export default [ { @@ -64,6 +66,16 @@ export default [ Role: "STAFF", }, }, + { + path: "/compete/report", + name: "competeReport", + component: CompeteReport, + meta: { + Auth: true, + Key: "SYS_EXAM_CONTEST_REPORT", + Role: "STAFF", + }, + }, { path: "/compete/period/add", name: "competePeriodAdd", diff --git a/src/modules/03_recruiting/views/01_compete/CompeteReport.vue b/src/modules/03_recruiting/views/01_compete/CompeteReport.vue new file mode 100644 index 000000000..7f2a4e5fc --- /dev/null +++ b/src/modules/03_recruiting/views/01_compete/CompeteReport.vue @@ -0,0 +1,369 @@ + + + + diff --git a/src/modules/04_registryPerson/components/TableView.vue b/src/modules/04_registryPerson/components/TableView.vue index aec0c46ef..6de071b64 100644 --- a/src/modules/04_registryPerson/components/TableView.vue +++ b/src/modules/04_registryPerson/components/TableView.vue @@ -1,5 +1,5 @@ @@ -57,4 +439,18 @@ function nextPage(type: string) { .q-item.hover-green { padding: 10px; } + +.tree-container { + overflow: auto; + height: 60vh; + border: 1px solid #e6e6e7; + border-radius: 10px; +} +.my-list-link { + color: rgb(118, 168, 222); + border-radius: 5px; + background: #a3d3fb48 !important; + font-weight: 600; + border: 1px solid rgba(175, 185, 196, 0.217); +} diff --git a/src/modules/09_leave/interface/index/Main.ts b/src/modules/09_leave/interface/index/Main.ts index 58ada0de4..0f0a59dd5 100644 --- a/src/modules/09_leave/interface/index/Main.ts +++ b/src/modules/09_leave/interface/index/Main.ts @@ -6,6 +6,7 @@ interface DataOption2 { id: number; name: string; } + interface DataDateMonthObject { month: number; year: number; diff --git a/src/modules/09_leave/router.ts b/src/modules/09_leave/router.ts index 6666df9f0..fe159a4b3 100644 --- a/src/modules/09_leave/router.ts +++ b/src/modules/09_leave/router.ts @@ -10,6 +10,9 @@ const ChangeRoundMain = () => const SpecialTimeMain = () => import("@/modules/09_leave/views/04_SpecialTimeMain.vue"); const leaveReport = () => import("@/modules/09_leave/views/06_ReportMain.vue"); + +const CheckinReport = () => + import("@/modules/09_leave/views/07_ReportCheckin.vue"); export default [ { path: "/round-time", @@ -91,24 +94,15 @@ export default [ Role: "STAFF", }, }, - // { - // path: "/statistics-report", - // name: "/statistics-report", - // component: reportMain, - // meta: { - // Auth: true, - // Key: [9], - // Role: "leave", - // }, - // }, - // { - // path: "/statistics-report/:type", - // name: "/statistics-report-detail", - // component: reportDetail, - // meta: { - // Auth: true, - // Key: [9], - // Role: "leave", - // }, - // }, + + { + path: "/checkinReport", + name: "checkinReport", + component: CheckinReport, + meta: { + Auth: true, + Key: "SYS_WORK_REPORT", + Role: "STAFF", + }, + }, ]; diff --git a/src/modules/09_leave/views/02_WorkingMain.vue b/src/modules/09_leave/views/02_WorkingMain.vue index 0335224c2..d28f5b1ab 100644 --- a/src/modules/09_leave/views/02_WorkingMain.vue +++ b/src/modules/09_leave/views/02_WorkingMain.vue @@ -4,7 +4,7 @@ import { ref } from "vue"; /** import Components */ import Tab1 from "@/modules/09_leave/components/02_WorkList/Tab1.vue"; import Tab2 from "@/modules/09_leave/components/02_WorkList/Tab2.vue"; -import DialogReport from "@/modules/09_leave/components/02_WorkList/DialogReport.vue"; +// import DialogReport from "@/modules/09_leave/components/02_WorkList/DialogReport.vue"; const tab = ref("1"); @@ -18,7 +18,7 @@ function onClickOpenDialog() {
รายการลงเวลาปฏิบัติงาน - รายงานสถิติการลงเวลา - + -->
@@ -60,7 +60,7 @@ function onClickOpenDialog() {
- + diff --git a/src/modules/09_leave/views/06_ReportMain.vue b/src/modules/09_leave/views/06_ReportMain.vue index a17e24433..c26c5863b 100644 --- a/src/modules/09_leave/views/06_ReportMain.vue +++ b/src/modules/09_leave/views/06_ReportMain.vue @@ -4,33 +4,59 @@ import { ref, onMounted } from "vue"; import { VuePDF, usePDF } from "@tato30/vue-pdf"; import { useQuasar } from "quasar"; -import http from "@/plugins/http"; -import config from "@/app.config"; +import { useRoute } from "vue-router"; import { checkPermission } from "@/utils/permissions"; import { useCounterMixin } from "@/stores/mixin"; +import { useStructureTree } from "@/stores/structureTree"; +import genReportXLSX from "@/plugins/genreportxlsx"; +import http from "@/plugins/http"; +import config from "@/app.config"; -import type { DataOption } from "@/modules/09_leave/interface/index/Main"; +import type { DataStructureTree } from "@/interface/main"; +import type { + DataOption, + DataDateMonthObject, +} from "@/modules/09_leave/interface/index/Main"; + +import LoadView from "@/components/LoadView.vue"; /** use*/ -const mixin = useCounterMixin(); const $q = useQuasar(); -const { showLoader, hideLoader, date2Thai, dateToISO, messageError } = mixin; - -const apiGenReport = - "https://report-server.frappet.synology.me/api/v1/report-template/xlsx"; +const route = useRoute(); +const { fetchStructureTree } = useStructureTree(); +const { + showLoader, + hideLoader, + date2Thai, + dateToISO, + messageError, + monthYear2Thai, +} = useCounterMixin(); const year = ref(new Date().getFullYear()); const dateStart = ref(new Date(year.value, 9, 1)); const dateEnd = ref(new Date(year.value + 1, 8, 30)); -const employeeClass = ref("employee"); +const dateMonth = ref({ + month: new Date().getMonth(), + year: new Date().getFullYear(), +}); + +const typeReport = ref(""); +const optionReport = ref([ + { id: "1", name: "รายงานการลางานตามประเภทการลา" }, + { id: "2", name: "รายงานการลางาน" }, +]); + +const employeeClass = ref("officer"); const yearType = ref("FULL"); const employeeClassMain = ref([ - { id: "employee", name: "ลูกจ้างประจำ กทม." }, { id: "officer", name: "ข้าราชการ กทม. สามัญ" }, + { id: "employee", name: "ลูกจ้างประจำ กทม." }, ]); const yearTypeOptionMain = ref([ { id: "FULL", name: "รายปี" }, + { id: "MONTH", name: "รายเดือน" }, { id: "FIRSTHAFT", name: "ครึ่งปีแรก" }, { id: "SECONDHAFT", name: "ครึ่งปีหลัง" }, ]); @@ -38,26 +64,76 @@ const employeeClassOption = ref(employeeClassMain.value); const yearTypeOptionOption = ref(yearTypeOptionMain.value); const detailReport = ref(); +const isReport = ref(false); +const isLoadPDF = ref(false); + +/** tree*/ +const filterTree = ref(""); +const nodeId = ref(""); +const nodeLevel = ref(0); +const node = ref([]); +const expanded = ref([]); + +async function fetchDataTree() { + node.value = await fetchStructureTree(route.meta.Key as string, true); +} + +function onSelectedNode(id: string, level: number) { + nodeId.value = id; + nodeLevel.value = level; + updateLeaveday(); +} /** function อัปเดทบัญชีแสดงวันลา */ async function updateLeaveday() { - if (yearType.value === "FULL") { - dateStart.value = new Date(year.value - 1, 9, 1); - dateEnd.value = new Date(year.value, 8, 30); - } else if (yearType.value === "FIRSTHAFT") { - dateStart.value = new Date(year.value - 1, 9, 1); - dateEnd.value = new Date(year.value, 2, 31); - } else if (yearType.value === "SECONDHAFT") { - dateStart.value = new Date(year.value, 3, 1); - dateEnd.value = new Date(year.value, 8, 30); + if (!nodeId.value || !typeReport.value) { + return false; } - fetchLeaveday( - employeeClass.value, - yearType.value, - dateStart.value, - dateEnd.value - ); + isReport.value = false; + isLoadPDF.value = true; + pdfSrc.value = undefined; + switch (yearType.value) { + case "FULL": + dateStart.value = new Date(year.value - 1, 9, 1); + dateEnd.value = new Date(year.value, 8, 30); + break; + + case "MONTH": + const mount = dateMonth.value.month + 1; + // วันเริ่มต้นของเดือน + dateStart.value = new Date(dateMonth.value.year, mount - 1, 1); + // วันสิ้นสุดของเดือนถัดไป + dateEnd.value = new Date(dateMonth.value.year, mount, 0); + break; + + case "FIRSTHAFT": + dateStart.value = new Date(year.value - 1, 9, 1); + dateEnd.value = new Date(year.value, 2, 31); + break; + + case "SECONDHAFT": + dateStart.value = new Date(year.value, 3, 1); + dateEnd.value = new Date(year.value, 8, 30); + break; + + default: + break; + } + + typeReport.value === "1" + ? fetchLeaveday( + employeeClass.value, + yearType.value, + dateStart.value, + dateEnd.value + ) + : fetchLeaveday2( + employeeClass.value, + yearType.value, + dateStart.value, + dateEnd.value + ); } /** @@ -73,25 +149,57 @@ async function fetchLeaveday( startDate: Date, endDate: Date ) { - showLoader(); const body = { - type: year === "FULL" ? "FULL" : "HAFT", + type: year === "FULL" ? "FULL" : year === "MONTH" ? "MONTH" : "HAFT", startDate: dateToISO(startDate), endDate: dateToISO(endDate), + nodeId: nodeId.value, + node: nodeLevel.value, }; await http .post(config.API.leaveReportLeaveday(type), body) .then(async (res) => { const data = res.data.result; - data && (await genReport(data)); + data && (await fetchDocumentTemplate(data)); + isReport.value = data ? true : false; detailReport.value = data; }) .catch((err) => { messageError($q, err); }) .finally(() => { - hideLoader(); + isLoadPDF.value = false; + }); +} + +async function fetchLeaveday2( + type: string, + year: string, + startDate: Date, + endDate: Date +) { + const body = { + type: year === "FULL" ? "FULL" : year === "MONTH" ? "MONTH" : "HAFT", + startDate: dateToISO(startDate), + endDate: dateToISO(endDate), + nodeId: nodeId.value, + node: nodeLevel.value, + }; + + await http + .post(config.API.leaveReportLeave2(type), body) + .then(async (res) => { + const data = res.data.result; + data && (await fetchDocumentTemplate(data)); + isReport.value = data ? true : false; + detailReport.value = data; + }) + .catch((err) => { + messageError($q, err); + }) + .finally(() => { + isLoadPDF.value = false; }); } @@ -99,7 +207,7 @@ async function fetchLeaveday( * function เรียกไฟล์ PDF * @param data ข้อมูลบัญชีวันลา */ -async function genReport(data: any) { +async function fetchDocumentTemplate(data: any) { await axios .post(`${config.API.reportTemplate}/xlsx`, data, { headers: { @@ -111,7 +219,6 @@ async function genReport(data: any) { .then(async (res) => { const blob = new Blob([res.data]); const objectUrl = URL.createObjectURL(blob); - fileBlob.value = blob; const pdfData = await usePDF(`${objectUrl}`); setTimeout(() => { @@ -129,53 +236,11 @@ async function genReport(data: any) { }); } -/** - * function เรียกไฟล์ XLSX - * @param data ข้อมูลบัญชีวันลา - */ -async function genReportXLSX(data: any) { - await axios - .post(`${config.API.reportTemplate}/xlsx`, data, { - headers: { - accept: - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - "content-Type": "application/json", - }, - responseType: "blob", - }) - .then(async (res) => { - const blob = new Blob([res.data]); - downloadReport(blob, "xlsx"); - }) - .catch(async (e) => { - messageError($q, JSON.parse(await e.response.data.text())); - }) - .finally(() => { - hideLoader(); - }); -} - -/** - * function Download ไฟล์ - * @param data ข้อมูลบัญชีวันลา - * @param type นามสกุลไฟล์ - */ -async function downloadReport(data: any, type: string) { - const link = document.createElement("a"); - var fileName = "บัญชีแสดงวันลา"; - link.href = window.URL.createObjectURL(new Blob([data])); - link.setAttribute("download", `${fileName}.${type}`); - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); -} - const splitterModel = ref(14); const numOfPages = ref(0); const page = ref(1); const pdfSrc = ref(); -// const modalFull = ref(false); -const fileBlob = ref(); + /** ไปหน้าต่อไปของรายงาน */ function nextPage() { if (page.value < numOfPages.value) { @@ -190,42 +255,23 @@ function backPage() { } } -/** - * function ค้นหาข้อมูล Option - * @param val คำค้นหา - * @param update function - * @param type ประเภท option - */ -function filterFnOptions(val: any, update: Function, type: string) { - switch (type) { - case "employeeClass": - update(() => { - employeeClassOption.value = employeeClassMain.value.filter( - (v: DataOption) => v.name.indexOf(val) > -1 - ); - }); - break; - case "yearType": - update(() => { - yearTypeOptionOption.value = yearTypeOptionMain.value.filter( - (v: DataOption) => v.name.indexOf(val) > -1 - ); - }); - break; - } +function monthYearThai(val: DataDateMonthObject) { + if (val == null) return ""; + else return monthYear2Thai(val.month, val.year); } onMounted(() => { - updateLeaveday(); + fetchDataTree(); }); + - + diff --git a/src/modules/09_leave/views/07_ReportCheckin.vue b/src/modules/09_leave/views/07_ReportCheckin.vue new file mode 100644 index 000000000..8bedf36d6 --- /dev/null +++ b/src/modules/09_leave/views/07_ReportCheckin.vue @@ -0,0 +1,649 @@ + + + + + diff --git a/src/modules/18_command/components/DialogCreateCommandORG.vue b/src/modules/18_command/components/DialogCreateCommandORG.vue index c2cc53cb5..621cc9e69 100644 --- a/src/modules/18_command/components/DialogCreateCommandORG.vue +++ b/src/modules/18_command/components/DialogCreateCommandORG.vue @@ -78,8 +78,13 @@ const dataMapToSend = computed(() => { firstName: i.firstName, lastName: i.lastName, citizenId: i.citizenId, + ...(props.systemName?.includes("SALARY") && { + amount: i.positionSalaryAmount, + amountSpecial: i.amountSpecial, + }), })); }); + //Table const rows = ref([]); const selected = ref([]); @@ -143,6 +148,7 @@ function onSubmit() { commandNo: commandNo.value, persons: selected.value ? dataMapToSend.value : [], }; + await http .post(config.API.command + `/person`, body) .then(async (res) => { diff --git a/src/modules/18_command/components/Step/0_Main.vue b/src/modules/18_command/components/Step/0_Main.vue index 6e8df6a8b..0129ef8c7 100644 --- a/src/modules/18_command/components/Step/0_Main.vue +++ b/src/modules/18_command/components/Step/0_Main.vue @@ -47,6 +47,7 @@ async function fetchData() { isDraft.value = data.isDraft; isSign.value = data.isSign; isAttachment.value = data.isAttachment; + store.isSalary = data.isSalary }) .catch((err) => { messageError($q, err); diff --git a/src/modules/18_command/components/Step/Dialog2_Salary.vue b/src/modules/18_command/components/Step/Dialog2_Salary.vue index 9e26f1061..b91933301 100644 --- a/src/modules/18_command/components/Step/Dialog2_Salary.vue +++ b/src/modules/18_command/components/Step/Dialog2_Salary.vue @@ -6,13 +6,14 @@ import http from "@/plugins/http"; import config from "@/app.config"; import { useCounterMixin } from "@/stores/mixin"; import { useCommandMainStore } from "@/modules/18_command/store/Main"; - +import { useCommandDetail } from "@/modules/18_command/store/DetailStore"; import DialogHeader from "@/components/DialogHeader.vue"; const $q = useQuasar(); const { showLoader, hideLoader, dialogConfirm, messageError, success } = useCounterMixin(); const store = useCommandMainStore(); +const storeDetail = useCommandDetail(); const props = defineProps({ getData: Function, @@ -36,6 +37,7 @@ const formData = reactive({ mouthSalaryAmount: null, remarkVertical: null, remarkHorizontal: null, + amountSpecial: null, }); const readonly = ref(false); @@ -167,12 +169,28 @@ watch( :class="getClass(!readonly)" mask="###,###,###,###,###,###" reverse-fill-mask - :readonly="readonly" + :readonly="!storeDetail.isSalary" :label="`${'เงินเดือน'}`" :rules="[(val: any) => !!val || `${'กรุณากรอกเงินเดือน'}`]" /> - +
+ +
{ const readonly = ref(false); const dataCommand = ref(); const status = ref(""); - + const isSalary = ref(false) function checkStep(val: string) { status.value = val; switch (val) { @@ -39,5 +39,6 @@ export const useCommandDetail = defineStore("commandDetailStore", () => { readonly, status, dataCommand, + isSalary }; });