diff --git a/public/option/option.json b/public/option/option.json index eb20dd0b..bce4fc0a 100644 --- a/public/option/option.json +++ b/public/option/option.json @@ -1004,6 +1004,48 @@ "label": "Ministry of Labour", "value": "MOL" } + ], + + "propertiesField": [ + { + "label": "Worker status", + "value": "workerStatus", + "type": "array" + }, + { + "label": "Work type", + "value": "workType", + "type": "array" + }, + { + "label": "Hospital name / Medical entitlement", + "value": "hospitalNEntitlement", + "type": "string" + }, + { + "label": "Reference Number (Ref)", + "value": "refNo", + "type": "string" + }, + { + "label": "Work Status", + "value": "workStatus", + "type": "string" + }, + { + "label": "Contact channels", + "value": "contactChannel", + "type": "string" + }, + { + "label": "Remark", + "value": "remark", + "type": "string" + }, + { "label": "Document Check", "value": "documentCheck", "type": "string" }, + { "label": "Duty", "value": "duty", "type": "string" }, + { "label": "Messenger", "value": "messenger", "type": "string" }, + { "label": "Form", "value": "designForm", "type": "string" } ] }, @@ -2013,6 +2055,48 @@ "label": "กรมแรงงาน", "value": "MOL" } + ], + + "propertiesField": [ + { + "label": "สถานะคนงาน", + "value": "workerStatus", + "type": "array" + }, + { + "label": "ประเภทงาน", + "value": "workType", + "type": "array" + }, + { + "label": "ชื่อ รพ./สิทธิการรักษา", + "value": "hospitalNEntitlement", + "type": "string" + }, + { + "label": "เลขที่อ้างอิง (Ref)", + "value": "refNo", + "type": "string" + }, + { + "label": "สถานะงาน", + "value": "workStatus", + "type": "string" + }, + { + "label": "ช่องทางการติดต่อ", + "value": "contactChannel", + "type": "string" + }, + { + "label": "หมายเหตุ", + "value": "remark", + "type": "string" + }, + { "label": "ตรวจสอบเอกสาร", "value": "documentCheck", "type": "string" }, + { "label": "อากร", "value": "duty", "type": "string" }, + { "label": "พนักงานส่งเอกสาร", "value": "messenger", "type": "string" }, + { "label": "ออกแบบฟอร์ม", "value": "designForm", "type": "string" } ] } } diff --git a/src/components/00_home/MenuItem.vue b/src/components/00_home/MenuItem.vue index 484eccbc..f75fa42d 100644 --- a/src/components/00_home/MenuItem.vue +++ b/src/components/00_home/MenuItem.vue @@ -15,6 +15,7 @@ defineProps<{ hidden?: boolean; disabled?: boolean; isax?: boolean; + tab?: string; color: | 'green' | 'red' @@ -28,8 +29,8 @@ defineProps<{ }[]; }>(); -function navigateTo(destination: string) { - router.push(`${destination}`); +function navigateTo(destination: string, tab?: string) { + router.push({ path: `${destination}`, query: tab ? { tab } : {} }); } @@ -44,7 +45,7 @@ function navigateTo(destination: string) { v-for="(v, i) in list.filter((item) => !item.hidden)" :key="i" :bordered="$q.dark.isActive" - @click="!v.disabled && navigateTo(v.value)" + @click="!v.disabled && navigateTo(v.value, v.tab)" > { :key="i" class="surface-2 bordered rounded q-px-xs" > - {{ optionStore.mapOption(att.fieldName ?? '') }} + {{ + optionStore.mapOption( + att.fieldName ?? '', + 'propertiesField', + ) + }} diff --git a/src/components/04_product-service/WorkManagementComponent.vue b/src/components/04_product-service/WorkManagementComponent.vue index 2bf5db83..e79872d9 100644 --- a/src/components/04_product-service/WorkManagementComponent.vue +++ b/src/components/04_product-service/WorkManagementComponent.vue @@ -584,7 +584,12 @@ watch( :key="i" class="surface-2 bordered rounded q-px-xs" > - {{ optionStore.mapOption(att.fieldName ?? '') }} + {{ + optionStore.mapOption( + att.fieldName ?? '', + 'propertiesField', + ) + }}
diff --git a/src/components/04_property-management/FormProperty.vue b/src/components/04_property-management/FormProperty.vue new file mode 100644 index 00000000..34067e8e --- /dev/null +++ b/src/components/04_property-management/FormProperty.vue @@ -0,0 +1,226 @@ + + + + + diff --git a/src/components/05_quotation/QuotationCard.vue b/src/components/05_quotation/QuotationCard.vue index e9f6885c..19f1a8bb 100644 --- a/src/components/05_quotation/QuotationCard.vue +++ b/src/components/05_quotation/QuotationCard.vue @@ -24,6 +24,7 @@ defineProps<{ hideAction?: boolean; useCancel?: boolean; disableCancel?: boolean; + useRejectCancel?: boolean; customData?: { label: string; @@ -41,6 +42,7 @@ defineEmits<{ (e: 'example'): void; (e: 'preview'): void; (e: 'cancel'): void; + (e: 'rejectCancel'): void; }>(); const rand = Math.random(); @@ -62,15 +64,16 @@ const rand = Math.random(); solid />
-
+
+
-
@@ -2186,7 +2186,6 @@ function covertToNode() {
- {{ optionStore.mapOption(att.fieldName) }} + {{ + optionStore.mapOption( + att.fieldName, + 'propertiesField', + ) + }}
diff --git a/src/pages/08_request-list/FormCancel.vue b/src/pages/08_request-list/FormCancel.vue new file mode 100644 index 00000000..4490418d --- /dev/null +++ b/src/pages/08_request-list/FormCancel.vue @@ -0,0 +1,21 @@ + + + + diff --git a/src/pages/08_request-list/MainPage.vue b/src/pages/08_request-list/MainPage.vue index 79488760..f1991e09 100644 --- a/src/pages/08_request-list/MainPage.vue +++ b/src/pages/08_request-list/MainPage.vue @@ -11,6 +11,9 @@ import NoData from 'src/components/NoData.vue'; import TableRequestList from './TableRequestList.vue'; import PaginationComponent from 'src/components/PaginationComponent.vue'; import PaginationPageSize from 'src/components/PaginationPageSize.vue'; +import DialogFormContainer from 'src/components/dialog/DialogFormContainer.vue'; +import DialogHeader from 'src/components/dialog/DialogHeader.vue'; +import FormCancel from './FormCancel.vue'; // NOTE: Stores & Type import { useNavigator } from 'src/stores/navigator'; @@ -19,6 +22,7 @@ import useFlowStore from 'src/stores/flow'; import { useRequestList } from 'src/stores/request-list'; import { RequestData, RequestDataStatus } from 'src/stores/request-list/types'; import { dialogWarningClose } from 'src/stores/utils'; +import { CancelButton, SaveButton } from 'src/components/button'; const $q = useQuasar(); const navigatorStore = useNavigator(); @@ -37,6 +41,9 @@ const pageState = reactive({ fieldSelected: [...column.map((v) => v.name)], gridView: false, total: 0, + rejectCancelDialog: false, + rejectCancelReason: '', + requestId: '', }); const fieldSelectedOption = computed(() => { @@ -93,6 +100,23 @@ function triggerView(opts: { requestData: RequestData }) { window.open(url.toString(), '_blank'); } +async function submitRejectCancel() { + const res = await requestListStore.rejectRequest(pageState.requestId, { + reason: pageState.rejectCancelReason, + }); + + if (res) { + const indexRequest = data.value?.findIndex( + (v) => v.id === pageState.requestId, + ); + + data.value[indexRequest].rejectRequestCancel = true; + data.value[indexRequest].rejectRequestCancelReason = + pageState.rejectCancelReason; + pageState.rejectCancelDialog = false; + } +} + onMounted(async () => { pageState.gridView = $q.screen.lt.md ? true : false; navigatorStore.current.title = 'requestList.title'; @@ -357,6 +381,13 @@ watch([() => pageState.inputSearch, () => pageState.statusFilter], () => { :visible-columns="pageState.fieldSelected" @view="(data) => triggerView({ requestData: data })" @delete="(data) => triggerCancel(data.id)" + @reject-cancel=" + (data) => { + pageState.rejectCancelDialog = true; + pageState.rejectCancelReason = ''; + pageState.requestId = data.id; + } + " /> diff --git a/src/pages/08_request-list/ProductExpansion.vue b/src/pages/08_request-list/ProductExpansion.vue index 788f4d0c..4d110dc3 100644 --- a/src/pages/08_request-list/ProductExpansion.vue +++ b/src/pages/08_request-list/ProductExpansion.vue @@ -20,6 +20,8 @@ const props = defineProps<{ imgUrl?: string; requestCancel?: boolean; requestCancelReason?: string; + rejectRequestCancel?: boolean; + rejectRequestCancelReason?: string; installmentInfo?: { total: number; paid?: number; @@ -37,7 +39,9 @@ function changeableStatus(currentStatus?: RequestWorkStatus) { case RequestWorkStatus.Ready: case RequestWorkStatus.Waiting: case RequestWorkStatus.InProgress: - return [RequestWorkStatus.Canceled]; + return props.requestCancel && !props.rejectRequestCancel + ? [RequestWorkStatus.Canceled, RequestWorkStatus.RejectCancel] + : [RequestWorkStatus.Canceled]; case RequestWorkStatus.Validate: case RequestWorkStatus.Ended: case RequestWorkStatus.Completed: @@ -45,12 +49,15 @@ function changeableStatus(currentStatus?: RequestWorkStatus) { return []; default: if (props.readonly) return []; + const statuses = [ + RequestWorkStatus.Ready, + RequestWorkStatus.Ended, + RequestWorkStatus.Canceled, + ]; if (props.orderAble) { - return [ - RequestWorkStatus.Ready, - RequestWorkStatus.Ended, - RequestWorkStatus.Canceled, - ]; + return props.requestCancel && !props.rejectRequestCancel + ? [...statuses, RequestWorkStatus.RejectCancel] + : statuses; } else { return [RequestWorkStatus.Ended, RequestWorkStatus.Canceled]; } @@ -101,13 +108,21 @@ function changeableStatus(currentStatus?: RequestWorkStatus) { @@ -171,10 +186,11 @@ function changeableStatus(currentStatus?: RequestWorkStatus) { clickable v-close-popup class="row items-center" + :class="{ 'bordered-t': value === 'RejectCancel' }" @click=" $emit('changeStatus', { step: status, - requestWorkStatus: value, + requestWorkStatus: value as RequestWorkStatus, }) " > diff --git a/src/pages/08_request-list/PropertiesExpansion.vue b/src/pages/08_request-list/PropertiesExpansion.vue index b7ac0250..b2b9baad 100644 --- a/src/pages/08_request-list/PropertiesExpansion.vue +++ b/src/pages/08_request-list/PropertiesExpansion.vue @@ -164,12 +164,15 @@ defineEmits<{ class="row items-center q-pb-sm" >
- {{ i + 1 }} {{ optionStore.mapOption(prop.fieldName) }} + {{ i + 1 }} + {{ optionStore.mapOption(prop.fieldName, 'propertiesField') }}
diff --git a/src/pages/08_request-list/RequestAction.vue b/src/pages/08_request-list/RequestAction.vue index b9fbf082..26f9c71e 100644 --- a/src/pages/08_request-list/RequestAction.vue +++ b/src/pages/08_request-list/RequestAction.vue @@ -33,7 +33,7 @@ enum Step { Configure = 2, } -const open = defineModel('selected', { default: false }); +const open = defineModel({ default: false }); const step = ref(Step.Product); const selected = ref<{ _work: RequestWork }[]>([]); const form = reactive({ diff --git a/src/pages/08_request-list/RequestListView.vue b/src/pages/08_request-list/RequestListView.vue index eae2002a..a78c9323 100644 --- a/src/pages/08_request-list/RequestListView.vue +++ b/src/pages/08_request-list/RequestListView.vue @@ -11,10 +11,13 @@ import PropertiesExpansion from './PropertiesExpansion.vue'; import FormGroupHead from './FormGroupHead.vue'; import AvatarGroup from 'src/components/shared/AvatarGroup.vue'; import { StateButton } from 'components/button'; -import { CancelButton, MainButton } from 'components/button'; +import { CancelButton, MainButton, SaveButton } from 'components/button'; import DutyExpansion from './DutyExpansion.vue'; import MessengerExpansion from './MessengerExpansion.vue'; import RequestAction from './RequestAction.vue'; +import DialogFormContainer from 'src/components/dialog/DialogFormContainer.vue'; +import DialogHeader from 'src/components/dialog/DialogHeader.vue'; +import FormCancel from './FormCancel.vue'; // NOTE: Store import { @@ -104,6 +107,9 @@ const pageState = reactive({ hideMetaData: false, currentStep: 1, requestActionDialog: false, + rejectCancelDialog: false, + rejectCancelReason: '', + requestWorkId: '', }); // NOTE: Function @@ -173,6 +179,14 @@ async function triggerChangeStatusWork(step: Step, responsibleUserId?: string) { }); return; } + + if (step.workStatus === 'RejectCancel') { + pageState.rejectCancelDialog = true; + pageState.rejectCancelReason = ''; + pageState.requestWorkId = step.requestWorkId; + return; + } + const res = await requestListStore.editStatusRequestWork(step); if (res) { const indexWork = workList.value?.findIndex( @@ -400,6 +414,30 @@ async function submitRequestAction(data: { pageState.requestActionDialog = false; } } + +async function submitRejectCancel() { + const current = route.params['requestListId']; + if (typeof current !== 'string') return; + + const res = await requestListStore.rejectRequestWork( + current, + pageState.requestWorkId, + { + reason: pageState.rejectCancelReason, + }, + ); + + if (res) { + const indexWork = workList.value?.findIndex( + (v) => v.id === pageState.requestWorkId, + ); + + workList.value[indexWork].rejectRequestCancel = true; + workList.value[indexWork].rejectRequestCancelReason = + pageState.rejectCancelReason; + pageState.rejectCancelDialog = false; + } +} diff --git a/src/pages/08_request-list/TableRequestList.vue b/src/pages/08_request-list/TableRequestList.vue index 359cf498..ed9d1227 100644 --- a/src/pages/08_request-list/TableRequestList.vue +++ b/src/pages/08_request-list/TableRequestList.vue @@ -32,6 +32,7 @@ const props = withDefaults( defineEmits<{ (e: 'view', data: RequestData): void; (e: 'delete', data: RequestData): void; + (e: 'rejectCancel', data: RequestData): void; }>(); function responsiblePerson(quotation: QuotationFull): CreatedBy[] | undefined { @@ -188,15 +189,24 @@ function getEmployeeName( props.row.customerRequestCancel && props.row.requestDataStatus !== RequestDataStatus.Canceled " - :hsla-color="'--red-5-hsl'" + :hsla-color=" + props.row.rejectRequestCancel ? '--blue-6-hsl' : '--red-5-hsl' + " + :title=" + props.row.rejectRequestCancel + ? $t('requestList.status.RejectedCancel') || '-' + : $t(`requestList.status.CancelRequested`) || '-' + " class="q-ml-sm" - :title="$t(`requestList.status.CancelRequested`) || '-'" > @@ -220,11 +230,15 @@ function getEmployeeName( hide-view hide-delete use-cancel + :use-reject-cancel=" + props.row.customerRequestCancel && !props.row.rejectRequestCancel + " :disable-cancel=" props.row.requestDataStatus === RequestDataStatus.Canceled || props.row.requestDataStatus === RequestDataStatus.Completed " @cancel="$emit('delete', props.row)" + @reject-cancel="$emit('rejectCancel', props.row)" /> @@ -242,6 +256,10 @@ function getEmployeeName( hide-kebab-edit hide-kebab-delete use-cancel + class="full-height" + :use-reject-cancel=" + props.row.customerRequestCancel && !props.row.rejectRequestCancel + " :disable-cancel=" props.row.requestDataStatus === RequestDataStatus.Canceled || props.row.requestDataStatus === RequestDataStatus.Completed @@ -285,6 +303,7 @@ function getEmployeeName( ]" @view="$emit('view', props.row)" @cancel="$emit('delete', props.row)" + @reject-cancel="$emit('rejectCancel', props.row)" > + + diff --git a/src/pages/09_task-order/MainPage.vue b/src/pages/09_task-order/MainPage.vue index 54df2966..a842b431 100644 --- a/src/pages/09_task-order/MainPage.vue +++ b/src/pages/09_task-order/MainPage.vue @@ -4,6 +4,7 @@ import { computed, onMounted, reactive, watch, ref } from 'vue'; import { storeToRefs } from 'pinia'; import { useQuasar } from 'quasar'; import { useI18n } from 'vue-i18n'; +import { useRoute } from 'vue-router'; // NOTE: Components import StatCardComponent from 'src/components/StatCardComponent.vue'; @@ -29,6 +30,7 @@ import { PaginationResult } from 'src/types'; const { t } = useI18n(); const $q = useQuasar(); +const route = useRoute(); const navigatorStore = useNavigator(); const flowStore = useFlowStore(); const taskOrderStore = useTaskOrderStore(); @@ -143,6 +145,10 @@ onMounted(async () => { navigatorStore.current.path = [{ text: 'taskOrder.caption', i18n: true }]; fetchTaskOrderList(); taskOrderStore.getTaskOrderStats(); + + if (route.query['tab'] && typeof route.query['tab'] === 'string') { + pageState.currentTab = route.query['tab']; + } }); watch( diff --git a/src/pages/12_debit-note/TableDebitNote.vue b/src/pages/12_debit-note/TableDebitNote.vue index f94bf9e6..de3d6907 100644 --- a/src/pages/12_debit-note/TableDebitNote.vue +++ b/src/pages/12_debit-note/TableDebitNote.vue @@ -59,7 +59,7 @@ const visible = computed(() => } & Omit[0], 'row'>" > - + @@ -303,7 +396,12 @@ watch([() => pastYears.value], async () => { style="margin-left: auto" :icon="'material-symbols:download'" :label="$t('general.download')" - @click.stop="reportStore.downloadReportReceipt()" + @click.stop=" + reportStore.downloadReportReceipt({ + startDate: state.date[0], + endDate: state.date[1], + }) + " /> @@ -345,7 +443,12 @@ watch([() => pastYears.value], async () => { style="margin-left: auto" :icon="'material-symbols:download'" :label="$t('general.download')" - @click.stop="reportStore.downloadReportInvoice()" + @click.stop=" + reportStore.downloadReportInvoice({ + startDate: state.date[0], + endDate: state.date[1], + }) + " /> @@ -386,7 +489,12 @@ watch([() => pastYears.value], async () => { style="margin-left: auto" :icon="'material-symbols:download'" :label="$t('general.download')" - @click.stop="reportStore.downloadReportInvoice()" + @click.stop=" + reportStore.downloadReportInvoice({ + startDate: state.date[0], + endDate: state.date[1], + }) + " /> @@ -409,7 +517,10 @@ watch([() => pastYears.value], async () => { :icon="'material-symbols:download'" :label="$t('general.download')" @click.stop=" - reportStore.downloadReportSale('by-customer') + reportStore.downloadReportSale('by-customer', { + startDate: state.date[0], + endDate: state.date[1], + }) " /> @@ -450,7 +561,10 @@ watch([() => pastYears.value], async () => { :icon="'material-symbols:download'" :label="$t('general.download')" @click.stop=" - reportStore.downloadReportSale('by-product-group') + reportStore.downloadReportSale('by-product-group', { + startDate: state.date[0], + endDate: state.date[1], + }) " /> @@ -482,7 +596,12 @@ watch([() => pastYears.value], async () => { style="margin-left: auto" :icon="'material-symbols:download'" :label="$t('general.download')" - @click.stop="reportStore.downloadReportSale('by-sale')" + @click.stop=" + reportStore.downloadReportSale('by-sale', { + startDate: state.date[0], + endDate: state.date[1], + }) + " /> @@ -636,6 +755,36 @@ watch([() => pastYears.value], async () => { + + diff --git a/src/pages/14_report/constants.ts b/src/pages/14_report/constants.ts index 2ff3a22c..e578e11e 100644 --- a/src/pages/14_report/constants.ts +++ b/src/pages/14_report/constants.ts @@ -1,6 +1,6 @@ import { QTableProps } from 'quasar'; -import { Invoice, Receipt } from 'src/stores/payment/types'; import { + CustomerDept, Report, ReportProduct, ReportQuotation, @@ -15,6 +15,7 @@ export enum ViewMode { Product = 'product', Sale = 'sale', Profit = 'profit', + DebtReport = 'debtReport', } type ColumnsSale = { @@ -48,6 +49,10 @@ type ColumnsProfixByYear = Omit & { income: number; }; +type ColumnsDebt = Omit & { + customerName: string; +}; + export const colReportQuotation = [ { name: 'code', @@ -260,10 +265,33 @@ export const colProfitByYear = [ field: (data: ColumnsProfixByYear) => formatNumberDecimal(data.income, 2), }, ] as const satisfies QTableProps['columns']; + +export const colProfitDebtReport = [ + { + name: 'customerName', + align: 'left', + label: 'report.debtReport.customerName', + field: (data: ColumnsDebt) => data.customerName, + }, + { + name: 'unpaid', + align: 'left', + label: 'report.debtReport.unpaid', + field: (data: ColumnsDebt) => formatNumberDecimal(data.unpaid, 2), + }, + { + name: 'paid', + align: 'left', + label: 'report.debtReport.paid', + field: (data: ColumnsDebt) => formatNumberDecimal(data.paid, 2), + }, +] as const satisfies QTableProps['columns']; + export const pageTabs = [ { label: 'Document', value: ViewMode.Document, by: ['user'] }, { label: 'Invoice', value: ViewMode.Invoice, by: ['user'] }, { label: 'Product', value: ViewMode.Product, by: ['user'] }, { label: 'Sale', value: ViewMode.Sale, by: ['admin'] }, { label: 'Profit', value: ViewMode.Profit, by: ['admin'] }, + { label: 'Debt', value: ViewMode.DebtReport, by: ['admin'] }, ]; diff --git a/src/pages/15_dash-board/MainPage.vue b/src/pages/15_dash-board/MainPage.vue index 8688f49b..3e757a9e 100644 --- a/src/pages/15_dash-board/MainPage.vue +++ b/src/pages/15_dash-board/MainPage.vue @@ -97,7 +97,7 @@ watch( teleport auto-apply for="select-date-range" - class="col-md-4 col" + class="col-md-3 col" v-model="state.date" :dark="$q.dark.isActive" :locale="$i18n.locale === 'tha' ? 'th' : 'en'" @@ -118,6 +118,13 @@ watch( + + {{ + dateFormatJS({ date: state.date[0] }) + + ' - ' + + dateFormatJS({ date: state.date[1] }) + }} + diff --git a/src/pages/15_dash-board/chart/ChartReceipt.vue b/src/pages/15_dash-board/chart/ChartReceipt.vue index 75258162..e7423fbc 100644 --- a/src/pages/15_dash-board/chart/ChartReceipt.vue +++ b/src/pages/15_dash-board/chart/ChartReceipt.vue @@ -1,10 +1,9 @@