diff --git a/src/css/quasar.variables.scss b/src/css/quasar.variables.scss
index a27588fd..a319cf02 100644
--- a/src/css/quasar.variables.scss
+++ b/src/css/quasar.variables.scss
@@ -181,3 +181,16 @@ div.fullscreen.q-drawer__backdrop {
i.q-icon.mdi.mdi-alert.q-table__bottom-nodata-icon {
color: #ffc224 !important;
}
+
+i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon {
+ color: hsl(var(--text-mute));
+}
+
+i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-item__toggle-icon--rotated {
+ color: var(--brand-1);
+}
+
+.q-item.q-item-type.row.no-wrap.q-item--dense.q-item--clickable.q-link.cursor-pointer.q-focusable.q-hoverable.surface-1
+ .q-focus-helper {
+ visibility: hidden;
+}
diff --git a/src/i18n/eng.ts b/src/i18n/eng.ts
index cb9c83c1..f1eeaa78 100644
--- a/src/i18n/eng.ts
+++ b/src/i18n/eng.ts
@@ -137,6 +137,8 @@ export default {
selected: 'Selected {number} {msg}',
next: 'Next',
import: 'Import',
+ numberOfDay: 'Number of days',
+ other: 'Other',
},
menu: {
@@ -905,6 +907,7 @@ export default {
InProgress: 'In Progress',
Completed: 'Completed',
Canceled: 'Canceled',
+
AwaitOrder: 'Awaiting Order',
ReadyOrder: 'Ready for Order',
EndOrder: 'Order Completed',
@@ -919,17 +922,62 @@ export default {
taskOrder: {
title: 'Task Order',
+ receive: 'Job Receipt ',
caption: 'All Task Order',
+ code: 'Task Order Code',
receiveTaskOrder: 'Receive Task Order',
+ tasktobeDone: 'Task to be Done',
+ inProgress: 'In Progress',
+ completed: 'Completed',
sendTaskOrder: 'Send Task Order',
payment: 'Payment',
goodReceipt: 'Good Receipt',
+ canceled: 'Canceled',
issueBranch: 'Issue Branch',
issueDate: 'Issue Date',
madeBy: 'Made By',
contactName: 'Contact Name',
+ workOrderCode: 'Work Order Code',
+ workOrderName: 'Work Order Name',
+ telephone: 'Telephone',
+
+ productList: 'Product List',
+ amountOfEmployee: 'Number of workers',
+
+ recipientOrSender: 'Recipient/Sender',
+ workStartDate: 'Work Start Date & Time',
+ workSubmissionDate: 'Work Submission Date & Time',
+ status: {
+ Pending: 'Pending',
+ InProgress: 'InProgress',
+ Success: 'Success',
+ Failed: 'Failed',
+ Redo: 'Redo',
+ Validate: 'Validate',
+ Complete: 'Complete',
+ Canceled: 'Canceled',
+ },
+
+ receiveTask: 'Receive Task',
+ receiveScan: 'Receive Task (Scan)',
+ receiveCustom: 'Receive Task (Custom)',
+
+ allProduct: 'All Product',
+ alreadySentTask: 'Already Sent Task',
+ sentTask: 'Sent Task',
+ taskInCart: 'Task in Cart',
+ waitReceive: 'Waiting to accept task',
+ failTaskOrderCode: 'Task order with issue',
+ describeIssue: 'Describe issue',
+ documentSubmitFailed: 'Document submission failed',
+ taskNotFullyCompleted: 'Task was not fully completed',
+
+ noRequestAvailable: 'There is no request list available for processing',
+ validate: 'Validate',
+ done: 'Done',
+ confirmValidate: 'Confirm Validate',
},
dialog: {
@@ -957,8 +1005,11 @@ export default {
headquartersNotEstablished: 'Headoffice not established',
warningClose: 'Incomplte edit data, Do you want to close?',
close: 'Do you want to close this window?',
-
confirmChangeStatus: 'Do you want to change your status?',
+ confirmSavingStatus:
+ 'Do you want to confirm the saving of the status change data?',
+ confirmSending: 'Do you confirm the submission of the task?',
+ confirmValidate: 'Do you confirm the validation?',
},
action: {
ok: 'OK',
@@ -1036,6 +1087,7 @@ export default {
installmentsValidateFailed:
'Validation failed. Each installment must include at least one product. Please review and update the installments accordingly.',
flowTemplateNotFound: 'Workflow template cannot be found',
+ taskOrderNotFound: 'Task order cannot be found.',
},
},
diff --git a/src/i18n/tha.ts b/src/i18n/tha.ts
index c2c943d1..d69fad9b 100644
--- a/src/i18n/tha.ts
+++ b/src/i18n/tha.ts
@@ -137,6 +137,8 @@ export default {
selected: '{number} {msg}ถูกเลือก',
import: 'นำเข้า',
next: 'ถัดไป',
+ numberOfDay: 'จำนวนวัน',
+ other: 'อื่นๆ',
},
menu: {
@@ -894,6 +896,7 @@ export default {
InProgress: 'ดำเนินการ',
Completed: 'เสร็จสิ้น',
Canceled: 'ยกเลิก',
+
AwaitOrder: 'รอสั่งงาน',
ReadyOrder: 'พร้อมสั่งงาน',
EndOrder: 'จบงาน',
@@ -908,17 +911,60 @@ export default {
taskOrder: {
title: 'ใบสั่งงาน',
+ receive: 'ใบรับงาน',
caption: 'ใบสั่งงานทั้งหมด',
+ code: 'เลขใบสั่งงาน',
receiveTaskOrder: 'รับใบสั่งงาน',
+ tasktobeDone: 'งานที่ต้องทำ',
+ inProgress: 'ดำเนินการ',
+ completed: 'เรียบร้อย',
sendTaskOrder: 'ส่งใบสั่งงาน',
payment: 'ขำระเงิน',
goodReceipt: 'ใบรับสินค้า',
-
+ canceled: 'ยกเลิก',
issueBranch: 'สาขาที่ออก',
issueDate: 'วันที่ออก',
madeBy: 'ผู้ที่ทำรายการ',
contactName: 'ชื่อผู้ติดต่อ',
+ workOrderCode: 'รหัสใบสั่งงาน',
+ workOrderName: 'ชื่อใบสั่งงาน',
+ telephone: 'เบอร์โทร',
+
+ productList: 'รายการสินค้า',
+ amountOfEmployee: 'จำนวนแรงงาน (คน)',
+ recipientOrSender: 'คนรับ/ส่งงาน',
+ workStartDate: 'วันที่เวลารับงาน',
+ workSubmissionDate: 'วันที่เวลาส่งงาน',
+ status: {
+ Pending: 'รอดำเนินการ',
+ InProgress: 'กำลังดำเนินการ ',
+ Success: 'ดำเนินการสำเร็จ',
+ Failed: 'ดำเนินการไม่สำเร็จ',
+ Redo: 'ทำใหม่',
+ Validate: 'ตรวจสอบความถูกต้อง',
+ Complete: 'ดำเนินการเสร็จสิ้น',
+ Canceled: ' ยกเลิกรายการคำขอ',
+ },
+
+ receiveTask: 'รับงาน',
+ receiveScan: 'รับงานแบบสแกน',
+ receiveCustom: 'รับงานแบบเลือกเอง',
+
+ allProduct: 'สินค้าทั้งหมด',
+ alreadySentTask: 'ส่งงานแล้ว',
+ sentTask: 'ส่งงาน',
+ taskInCart: 'งานในตะกร้า',
+ waitReceive: 'รอรับงาน',
+ failTaskOrderCode: 'เลขที่ใบรายการที่พบปัญหา',
+ describeIssue: 'ระบุปัญหา',
+ documentSubmitFailed: 'ยื่นเอกสารไม่ผ่าน',
+ taskNotFullyCompleted: 'ทำรายการไม่ครบ',
+
+ noRequestAvailable: 'ไม่มีใบรายการคำขอที่สามารถดำเนินการได้',
+ validate: 'ตรวจสอบสินค้า',
+ done: 'ดำเนินการแล้ว',
+ confirmValidate: 'ยืนยันการตรวจสอบ',
},
dialog: {
@@ -946,6 +992,10 @@ export default {
warningClose: 'มีการแก้ไขที่ยังไม่ได้บันทึก คุณต้องการปิดใช่หรือไม่',
close: 'คุณต้องการปิดหน้าต่างนี้ใช่หรือไม่',
confirmChangeStatus: 'คุณต้องการเปลี่ยนสถานะใช่หรือไม่',
+ confirmSavingStatus:
+ 'คุณต้องการยืนยันการบันทึกข้อมูลการเปลี่ยนสถานะใช่หรือไม่',
+ confirmSending: 'ยืนยันการส่งงานใช่หรือไม่',
+ confirmValidate: 'ยืนยันการตรวจสอบใช่หรือไม่',
},
action: {
ok: 'ยืนยัน',
@@ -1020,6 +1070,7 @@ export default {
installmentsValidateFailed:
'ข้อมูลงวดไม่ถูกต้อง กรุณาตรวจสอบและยืนยันว่าแต่ละงวดมีสินค้าอย่างน้อยหนึ่งรายการ',
flowTemplateNotFound: 'ไม่พบขั้นตอนการทำงาน',
+ taskOrderNotFound: 'ไม่พบใบสั่งงาน',
},
},
diff --git a/src/pages/01_branch-management/MainPage.vue b/src/pages/01_branch-management/MainPage.vue
index 72ac7d41..0e313053 100644
--- a/src/pages/01_branch-management/MainPage.vue
+++ b/src/pages/01_branch-management/MainPage.vue
@@ -6,7 +6,7 @@ import { Icon } from '@iconify/vue';
import { BranchContact } from 'stores/branch-contact/types';
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
-import type { QTableProps } from 'quasar';
+import type { QTableProps, QTableSlots } from 'quasar';
import { resetScrollBar } from 'src/stores/utils';
import useBranchStore from 'stores/branch';
import useFlowStore from 'stores/flow';
@@ -1330,7 +1330,11 @@ watch(currentHq, () => {
-
+
@@ -203,9 +208,13 @@ function calculateInstallments(param: {
@@ -485,6 +495,10 @@ function calculateInstallments(param: {
--_color: var(--green-6-hsl);
}
+.task-order-color {
+ --_color: var(--pink-7-hsl);
+}
+
.bg-color {
color: white;
background: hsla(var(--_color));
diff --git a/src/pages/05_quotation/QuotationFormProductSelect.vue b/src/pages/05_quotation/QuotationFormProductSelect.vue
index aae8ea66..ca480c52 100644
--- a/src/pages/05_quotation/QuotationFormProductSelect.vue
+++ b/src/pages/05_quotation/QuotationFormProductSelect.vue
@@ -493,6 +493,7 @@ watch(
state.search, getWorkerList);
@click="state.importWorker = true"
:label="$t('quotation.importWorker')"
/>
+
diff --git a/src/pages/08_request-list/MainPage.vue b/src/pages/08_request-list/MainPage.vue
index b3fcb5e8..bc99f50f 100644
--- a/src/pages/08_request-list/MainPage.vue
+++ b/src/pages/08_request-list/MainPage.vue
@@ -151,6 +151,12 @@ watch([() => pageState.inputSearch, () => pageState.statusFilter], () =>
label: 'requestList.status.Completed',
color: 'light-green',
},
+ // {
+ // icon: 'mdi-close-circle-outline',
+ // count: 0,
+ // label: 'requestList.status.Canceled',
+ // color: 'red',
+ // },
]"
:dark="$q.dark.isActive"
/>
diff --git a/src/pages/08_request-list/ProductExpansion.vue b/src/pages/08_request-list/ProductExpansion.vue
index e0932ded..ed104a0a 100644
--- a/src/pages/08_request-list/ProductExpansion.vue
+++ b/src/pages/08_request-list/ProductExpansion.vue
@@ -35,6 +35,7 @@ defineProps<{
installmentNo?: number;
paySuccess: boolean;
payCondition: PayCondition;
+ readonly?: boolean;
}>();
// NOTE: Function
@@ -85,6 +86,7 @@ defineProps<{
-
+ })
+ "
+ :status-active="pageState.currentStep === value.order"
+ :label="value.name"
+ />
+
+
@@ -543,106 +538,10 @@ function isInstallmentPaySuccess(installmentNo: number) {
/>
-
-
// NOTE: Library
-import { computed, onMounted, reactive } from 'vue';
+import { computed, onMounted, reactive, watch, ref } from 'vue';
import { storeToRefs } from 'pinia';
// NOTE: Components
@@ -8,26 +8,47 @@ import StatCardComponent from 'src/components/StatCardComponent.vue';
import NoData from 'src/components/NoData.vue';
import PaginationComponent from 'src/components/PaginationComponent.vue';
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
+import TableTaskOrder from './TableTaskOrder.vue';
+import FloatingActionButton from 'components/FloatingActionButton.vue';
+import ReceiveDialog from './receive_view/ReceiveDialog.vue';
// NOTE: Stores & Type
+import {
+ TaskOrder,
+ TaskOrderStatus,
+ UserTaskStatus,
+} from 'src/stores/task-order/types';
import { useNavigator } from 'src/stores/navigator';
+import { useTaskOrderStore } from 'src/stores/task-order';
+import { useTaskOrderForm } from './form';
import useFlowStore from 'src/stores/flow';
-import { pageTabs, column } from './constants';
+import { pageTabs, column, pageTabsReceive } from './constants';
+import { isRoleInclude } from 'src/stores/utils';
+import { PaginationResult } from 'src/types';
+const taskOrderFormStore = useTaskOrderForm();
const navigatorStore = useNavigator();
const flowStore = useFlowStore();
+const taskOrderStore = useTaskOrderStore();
+const { stats, pageMax, page, data, pageSize } = storeToRefs(taskOrderStore);
// NOTE: Variable
const pageState = reactive({
- currentTab: 'title',
+ currentTab: 'Pending',
hideStat: false,
statusFilter: 'None',
inputSearch: '',
fieldSelected: [...column.map((v) => v.name)],
gridView: false,
total: 0,
+ isMessenger: isRoleInclude(['messenger']),
+
+ receiveDialog: false,
+ isReceiveScan: false,
});
+const taskOrderList = ref([]);
+
const fieldSelectedOption = computed(() => {
return column.map((v) => ({
label: v.label,
@@ -36,12 +57,118 @@ const fieldSelectedOption = computed(() => {
});
// NOTE: Function
+async function fetchTaskOrderList(opts?: { page?: number; pageSize?: number }) {
+ let res: PaginationResult | null;
+
+ if (pageState.isMessenger) {
+ res = await taskOrderStore.getUserTaskOrderList({
+ page: opts?.page || page.value,
+ pageSize: opts?.pageSize || pageSize.value,
+ query: pageState.inputSearch === '' ? undefined : pageState.inputSearch,
+ userTaskStatus: pageState.currentTab as UserTaskStatus,
+ });
+ } else {
+ res = await taskOrderStore.getTaskOrderList({
+ page: opts?.page || page.value,
+ pageSize: opts?.pageSize || pageSize.value,
+ query: pageState.inputSearch === '' ? undefined : pageState.inputSearch,
+ taskOrderStatus: pageState.currentTab as TaskOrderStatus | undefined,
+ });
+ }
+ if (res) {
+ data.value = res.result;
+ pageState.total = res.total;
+ pageMax.value = Math.ceil(res.total / pageSize.value);
+ taskOrderList.value = res.result;
+ }
+}
+
+async function triggerTaskOrder(opts: {
+ statusDialog: 'info' | 'edit' | 'create';
+ id?: string;
+}) {
+ const url = new URL(
+ `/task-order/order/${opts.statusDialog === 'create' ? 'add' : opts.id}`,
+ window.location.origin,
+ );
+
+ window.open(url.toString(), '_blank');
+}
+
+async function triggerTaskReceive(opts: {
+ statusDialog: 'info' | 'edit';
+ id?: string;
+}) {
+ const url = new URL(`/task-order/receive/${opts.id}`, window.location.origin);
+
+ window.open(url.toString(), '_blank');
+}
+
+async function submitReceiveTask(selectedTask: TaskOrder[]) {
+ const arrId = selectedTask.map((t) => t.id);
+ await taskOrderStore.acceptTaskOrder(arrId);
+ fetchTaskOrderList();
+ pageState.receiveDialog = false;
+}
+
+function openReceiveDialog(scan?: boolean) {
+ if (scan) pageState.isReceiveScan = scan;
+ pageState.receiveDialog = true;
+}
+
onMounted(async () => {
navigatorStore.current.title = 'taskOrder.title';
navigatorStore.current.path = [{ text: 'taskOrder.caption', i18n: true }];
+ fetchTaskOrderList();
+ taskOrderStore.getTaskOrderStats();
});
+
+watch(
+ [
+ () => pageState.currentTab,
+ () => pageState.inputSearch,
+ () => pageSize.value,
+ () => pageState.statusFilter,
+ ],
+ () => {
+ fetchTaskOrderList();
+ },
+);
+ {
+ if (!pageState.isMessenger) {
+ triggerTaskOrder({ statusDialog: 'create' });
+ }
+ }
+ "
+ >
+
+
+
+
@@ -54,9 +181,7 @@ onMounted(async () => {
color: hsl(var(--info-bg));
"
>
-
- 0
-
+ {{ pageState.total }}
{
@@ -142,12 +274,11 @@ onMounted(async () => {
-
-
{
},
{
label: $t('requestList.status.Pending'),
- value: '',
+ value: 'Pending',
},
{
label: $t('requestList.status.InProgress'),
- value: '',
+ value: 'InProgress',
},
{
label: $t('requestList.status.Completed'),
- value: '',
+ value: 'Complete',
},
]"
- />
+ /> -->
{
-
+
+ {
+ pageState.isMessenger
+ ? triggerTaskReceive({ statusDialog: 'info', id: v.id })
+ : triggerTaskOrder({ statusDialog: 'info', id: v.id });
+ }
+ "
+ />
+
-
-
-
+
+
+
diff --git a/src/pages/09_task-order/SelectReadyRequestWork.vue b/src/pages/09_task-order/SelectReadyRequestWork.vue
new file mode 100644
index 00000000..49091b73
--- /dev/null
+++ b/src/pages/09_task-order/SelectReadyRequestWork.vue
@@ -0,0 +1,302 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/09_task-order/TableEmployee.vue b/src/pages/09_task-order/TableEmployee.vue
new file mode 100644
index 00000000..114efaf6
--- /dev/null
+++ b/src/pages/09_task-order/TableEmployee.vue
@@ -0,0 +1,464 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t(`taskOrder.status.Success`) }}
+
+
+
+ {{ $t(`taskOrder.status.Failed`) }}
+
+
+
+
+
+
+
+ {{ col.label && $t(col.label) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ props.rowIndex + 1 }}
+
+
+
+ {{ props.row.request.code }}
+
+
+
+
+ {{ props.row._template.templateName }}
+
+ {{ $t('flow.stepNo', { msg: props.row._template.step }) }}
+ {{ props.row._template.templateStepName }}
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ getEmployeeName(props.row, { locale: $i18n.locale }) }}
+
+
+
+ {{ props.row.request.employee.code }}
+
+
+
+
+
+ {{ calculateAge(props.row.request.employee.dateOfBirth) }}
+
+ {{
+ useOptionStore().mapOption(props.row.request.employee.nationality)
+ }}
+
+
+ {{
+ dateFormatJS({
+ date: props.row.request.quotation.dueDate,
+ locale: $i18n.locale,
+ dayStyle: '2-digit',
+ monthStyle: 'short',
+ })
+ }}
+
+
+
+
+
+
+ {{ props.row.request.quotation.code }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/09_task-order/TableTaskOrder.vue b/src/pages/09_task-order/TableTaskOrder.vue
new file mode 100644
index 00000000..4922ec81
--- /dev/null
+++ b/src/pages/09_task-order/TableTaskOrder.vue
@@ -0,0 +1,301 @@
+
+
+
+
+
+
+
+
+
+ {{ col.label && $t(col.label) }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ props.rowIndex + 1 }}
+
+
+
+ {{ props.row.taskName || '-' }}
+
+ {{ props.row.taskName || '-' }}
+
+
+
+ {{ props.row.code || '-' }}
+
+
+ -
+
+ {{
+ $i18n.locale === 'eng'
+ ? props.row.institution.nameEN || '-'
+ : props.row.institution.name || '-'
+ }}
+
+
+ {{
+ dateFormatJS({
+ date: props.row.createdAt,
+ dayStyle: '2-digit',
+ monthStyle: '2-digit',
+ }) || '-'
+ }}
+ {{ dateFormat(props.row.createdAt, false, true) }}
+
+
+ {{ getCreatedByName(props.row, $i18n) }}
+
+
+ {{ props.row.contactTel || '-' }}
+
+
+ {{ props.row.contactName || '-' }}
+
+
+
+
+
+
+
+
+
+
+ {
+ openList(props.rowIndex, props.row);
+ }
+ "
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/09_task-order/TaskStatusComponent.vue b/src/pages/09_task-order/TaskStatusComponent.vue
new file mode 100644
index 00000000..589e2065
--- /dev/null
+++ b/src/pages/09_task-order/TaskStatusComponent.vue
@@ -0,0 +1,176 @@
+
+
+
+
+ {{ $t(`taskOrder.status.${status}`) }}
+
+
+
+
+
+
+
+ {{ $t(`taskOrder.status.${v.value}`) }}
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/09_task-order/constants.ts b/src/pages/09_task-order/constants.ts
index 31ae6404..e2bf30c6 100644
--- a/src/pages/09_task-order/constants.ts
+++ b/src/pages/09_task-order/constants.ts
@@ -1,11 +1,99 @@
import { QTableProps } from 'quasar';
+import { TaskStatus } from 'src/stores/task-order/types';
+
+export const taskStatusOpts = [
+ {
+ status: TaskStatus.Pending,
+ name: 'taskOrder.status.Pending',
+ },
+ {
+ status: TaskStatus.InProgress,
+ name: 'taskOrder.status.InProgress',
+ },
+ {
+ status: TaskStatus.Success,
+ name: 'taskOrder.status.Success',
+ },
+ {
+ status: TaskStatus.Failed,
+ name: 'taskOrder.status.Failed',
+ },
+
+ {
+ status: TaskStatus.Redo,
+ name: 'taskOrder.status.Redo',
+ },
+
+ {
+ status: TaskStatus.Validate,
+ name: 'taskOrder.status.Validate',
+ },
+
+ {
+ status: TaskStatus.Complete,
+ name: 'taskOrder.status.Complete',
+ },
+];
export const pageTabs = [
- { label: 'title', value: 'title' },
- { label: 'receiveTaskOrder', value: 'receiveTaskOrder' },
- { label: 'sendTaskOrder', value: 'sendTaskOrder' },
- { label: 'payment', value: 'payment' },
- { label: 'goodReceipt', value: 'goodReceipt' },
+ { label: 'title', value: 'Pending' },
+ { label: 'inProgress', value: 'InProgress' },
+ { label: 'goodReceipt', value: 'Complete' },
+];
+
+export const pageTabsReceive = [
+ { label: 'taskInCart', value: 'Pending' },
+ { label: 'receiveTask', value: 'Accept' },
+ { label: 'sentTask', value: 'Submit' },
+];
+
+export enum Status {
+ taskOrder = 'taskOrder',
+ receiveTaskOrder = 'receiveTaskOrder',
+ sendTaskOrder = 'sendTaskOrder',
+ payment = 'payment',
+ goodReceipt = 'goodReceipt',
+}
+
+export const taskStatusReceiveToggle = [
+ {
+ value: TaskStatus.Pending,
+ icon: 'mdi-file-move-outline',
+ color: 'warning',
+ },
+ {
+ value: TaskStatus.Success,
+ icon: 'mdi-file-check-outline',
+ color: 'positive',
+ },
+ {
+ value: TaskStatus.Failed,
+ icon: 'mdi-file-remove-outline',
+ color: 'negative',
+ },
+];
+
+export const taskStatusOrderToggle = [
+ {
+ value: TaskStatus.InProgress,
+ icon: 'mdi-file-move-outline',
+ color: 'positive',
+ },
+ {
+ value: TaskStatus.Complete,
+ icon: 'mdi-file-check-outline',
+ color: 'positive',
+ },
+ {
+ value: TaskStatus.Redo,
+ icon: 'mdi-file-remove-outline',
+ color: 'negative',
+ },
+ {
+ value: TaskStatus.Canceled,
+ icon: 'mdi-file-remove-outline',
+ color: 'negative',
+ },
];
export const column = [
@@ -16,40 +104,40 @@ export const column = [
field: 'no',
},
{
- name: 'taskOrder',
- align: 'left',
+ name: 'taskName',
+ align: 'center',
label: 'taskOrder.title',
- field: 'taskOrder',
+ field: 'taskName',
},
{
name: 'issueBranch',
- align: 'left',
+ align: 'center',
label: 'taskOrder.issueBranch',
field: 'issueBranch',
},
{
- name: 'agencies',
- align: 'left',
+ name: 'institution',
+ align: 'center',
label: 'general.agencies',
- field: 'agencies',
+ field: 'institution',
},
{
- name: 'issueDate',
- align: 'left',
+ name: 'createdAt',
+ align: 'center',
label: 'taskOrder.issueDate',
- field: 'issueDate',
+ field: 'createdAt',
},
{
- name: 'madeBy',
- align: 'left',
+ name: 'createdBy',
+ align: 'center',
label: 'taskOrder.madeBy',
- field: 'madeBy',
+ field: 'createdBy',
},
{
- name: 'telephone',
- align: 'left',
+ name: 'contactTel',
+ align: 'center',
label: 'general.telephone',
- field: 'telephone',
+ field: 'contactTel',
},
{
name: 'contactName',
@@ -58,9 +146,174 @@ export const column = [
field: 'contactName',
},
{
- name: 'status',
- align: 'left',
+ name: 'taskStatus',
+ align: 'center',
label: 'general.status',
- field: 'status',
+ field: 'taskStatus',
},
-];
+] as const satisfies QTableProps['columns'];
+
+export const employeeColumn = [
+ {
+ name: 'order',
+ align: 'center',
+ label: 'general.order',
+ field: 'no',
+ },
+ {
+ name: 'code',
+ align: 'center',
+ label: 'requestList.requestListCode',
+ field: 'code',
+ },
+ {
+ name: 'fullName',
+ align: 'center',
+ label: 'quotation.employeeName',
+ field: 'fullName',
+ },
+ {
+ name: 'dateOfBirth',
+ align: 'center',
+ label: 'general.age',
+ field: 'dateOfBirth',
+ },
+ {
+ name: 'nationality',
+ align: 'center',
+ label: 'general.nationality',
+ field: 'nationality',
+ },
+ {
+ name: 'dueDate',
+ align: 'center',
+ label: 'quotation.documentExpireDate',
+ field: 'dueDate',
+ },
+ {
+ name: 'day',
+ align: 'center',
+ label: 'general.numberOfDay',
+ field: 'day',
+ },
+ {
+ name: 'quotationCode',
+ align: 'center',
+ label: 'requestList.quotationCode',
+ field: 'quotationCode',
+ },
+] as const satisfies QTableProps['columns'];
+
+export const productColumn = [
+ {
+ name: 'order',
+ align: 'center',
+ label: 'general.order',
+ field: 'no',
+ },
+ {
+ name: 'code',
+ align: 'center',
+ label: 'productService.product.code',
+ field: 'code',
+ },
+ {
+ name: 'productList',
+ align: 'center',
+ label: 'taskOrder.productList',
+ field: 'productList',
+ },
+ {
+ name: 'amountOfEmployee',
+ align: 'center',
+ label: 'taskOrder.amountOfEmployee',
+ field: 'amountOfEmployee',
+ },
+ {
+ name: 'pricePerUnit',
+ align: 'center',
+ label: 'quotation.pricePerUnit',
+ field: 'pricePerUnit',
+ },
+ {
+ name: 'discount',
+ align: 'center',
+ label: 'general.discount',
+ field: 'discount',
+ },
+ {
+ name: 'priceBeforeVat',
+ align: 'center',
+ label: 'quotation.priceBeforeVat',
+ field: 'priceBeforeVat',
+ },
+ {
+ name: 'vat',
+ align: 'center',
+ label: 'general.vat',
+ field: 'vat',
+ },
+ {
+ name: 'totalPriceBaht',
+ align: 'center',
+ label: 'quotation.totalPriceBaht',
+ field: 'totalPriceBaht',
+ },
+] as const satisfies QTableProps['columns'];
+
+export const receiveProductColumn = [
+ {
+ name: 'order',
+ align: 'center',
+ label: 'general.order',
+ field: 'order',
+ },
+ {
+ name: 'requestListCode',
+ align: 'center',
+ label: 'requestList.requestListCode',
+ field: 'requestListCode',
+ },
+ {
+ name: 'flow',
+ align: 'center',
+ label: 'flow.step',
+ field: 'flow',
+ },
+ {
+ name: 'quotation',
+ align: 'center',
+ label: 'quotation.employeeName',
+ field: 'quotation',
+ },
+ {
+ name: 'age',
+ align: 'center',
+ label: 'general.age',
+ field: 'age',
+ },
+ {
+ name: 'nationality',
+ align: 'center',
+ label: 'general.nationality',
+ field: 'nationality',
+ },
+ {
+ name: 'documentExpireDate',
+ align: 'center',
+ label: 'quotation.documentExpireDate',
+ field: 'documentExpireDate',
+ },
+ {
+ name: 'numberOfDay',
+ align: 'center',
+ label: 'general.numberOfDay',
+ field: 'numberOfDay',
+ },
+ {
+ name: 'quotationCode',
+ align: 'center',
+ label: 'requestList.quotationCode',
+ field: 'quotationCode',
+ },
+] as const satisfies QTableProps['columns'];
diff --git a/src/pages/09_task-order/document_view/MainPage.vue b/src/pages/09_task-order/document_view/MainPage.vue
new file mode 100644
index 00000000..e36e586c
--- /dev/null
+++ b/src/pages/09_task-order/document_view/MainPage.vue
@@ -0,0 +1,519 @@
+
+
+
+
+
+
+
+
+
+ {{ $t('taskOrder.goodReceipt') }}
+
+
+
+
+
+ | {{ $t('preview.rank') }} |
+ {{ $t('preview.productCode') }} |
+ {{ $t('general.detail') }} |
+ {{ $t('general.amount') }} |
+ {{ $t('preview.pricePerUnit') }} |
+ {{ $t('preview.discount') }} |
+ {{ $t('preview.vat') }} |
+ {{ $t('preview.value') }} |
+
+
+ | {{ i + 1 }} |
+ {{ v.code }} |
+ {{ v.detail }} |
+ {{ v.amount }} |
+
+ {{ formatNumberDecimal(v.priceUnit, 2) }}
+ |
+
+ {{ formatNumberDecimal(v.discount, 2) }}
+ |
+
+ {{ formatNumberDecimal(v.vat, 2) }}
+ |
+
+ {{ formatNumberDecimal(v.value, 2) }}
+ |
+
+
+
+
+
+
+
+ | {{ $t('general.total') }} |
+
+ {{ formatNumberDecimal(summaryPrice.totalPrice, 2) }}
+ ฿
+ |
+
+
+
+ | {{ $t('general.discount') }} |
+
+ {{ formatNumberDecimal(summaryPrice.totalDiscount, 2) || 0 }} ฿
+ |
+
+
+ | {{ $t('general.totalAfterDiscount') }} |
+
+ {{
+ formatNumberDecimal(
+ summaryPrice.totalPrice - summaryPrice.totalDiscount,
+ 2,
+ )
+ }}
+ ฿
+ |
+
+
+
+ | {{ $t('general.totalVatExcluded') }} |
+
+ {{ formatNumberDecimal(summaryPrice.vatExcluded, 2) || 0 }}
+ ฿
+ |
+
+
+ | {{ $t('general.vat', { msg: '7%' }) }} |
+
+ {{ formatNumberDecimal(summaryPrice.vat, 2) }} ฿
+ |
+
+
+ | {{ $t('general.totalVatIncluded') }} |
+
+ {{
+ formatNumberDecimal(
+ summaryPrice.totalPrice -
+ summaryPrice.totalDiscount +
+ summaryPrice.vat,
+ 2,
+ )
+ }}
+ ฿
+ |
+
+
+
+
+
+
+ ({{ ThaiBahtText(summaryPrice.finalPrice) }})
+
+
+ ยอดรวมสุทธิ
+
+ {{
+ formatNumberDecimal(Math.max(summaryPrice.finalPrice, 0), 2) || 0
+ }}
+ ฿
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/09_task-order/document_view/ViewFooter.vue b/src/pages/09_task-order/document_view/ViewFooter.vue
new file mode 100644
index 00000000..fc9a34a5
--- /dev/null
+++ b/src/pages/09_task-order/document_view/ViewFooter.vue
@@ -0,0 +1,98 @@
+
+
+
+
+
+
diff --git a/src/pages/09_task-order/document_view/ViewHeader.vue b/src/pages/09_task-order/document_view/ViewHeader.vue
new file mode 100644
index 00000000..345c483d
--- /dev/null
+++ b/src/pages/09_task-order/document_view/ViewHeader.vue
@@ -0,0 +1,148 @@
+
+
+
+
+
+

+
+
+ {{ $t('taskOrder.goodReceipt') }}
+
+
+
+
+
+
+
+ {{ !!branch.virtual ? '' : $t('general.company') }} {{ branch.name }}
+
+
+
+ {{
+ formatAddress({
+ address: branch.address,
+ addressEN: branch.addressEN,
+ moo: branch.moo,
+ mooEN: branch.mooEN,
+ soi: branch.soi,
+ soiEN: branch.soiEN,
+ street: branch.street,
+ streetEN: branch.streetEN,
+ province: branch.province,
+ district: branch.district,
+ subDistrict: branch.subDistrict,
+ })
+ }}
+
+ เลขประจำตัวผู้เสียภาษี {{ branch.taxNo }}
+ เบอร์โทร {{ branch.telephoneNo }}
+ {{ branch.webUrl }}
+
+
+ ลูกค้า
+
+ {{
+ formatAddress({
+ address: institution.address,
+ addressEN: institution.addressEN,
+ moo: institution.moo || '-',
+ mooEN: institution.mooEN || '-',
+ soi: institution.soi || '-',
+ soiEN: institution.soiEN || '-',
+ street: institution.street || '-',
+ streetEN: institution.streetEN || '-',
+ province: institution.province,
+ district: institution.district,
+ subDistrict: institution.subDistrict,
+ })
+ }}
+
+
+
+
+
+
+ {{ $t('general.itemNo', { msg: `${$t('preview.taskOrder')}` }) }}
+
+
{{ details.code }}
+
+
+
ชื่องาน
+
{{ details.name }}
+
+
+
ผู้ติดต่อ
+
{{ details.contactName }}
+
+
+
+
+
+
diff --git a/src/pages/09_task-order/expansion/AdditionalFileExpansion.vue b/src/pages/09_task-order/expansion/AdditionalFileExpansion.vue
new file mode 100644
index 00000000..e6614f88
--- /dev/null
+++ b/src/pages/09_task-order/expansion/AdditionalFileExpansion.vue
@@ -0,0 +1,55 @@
+
+
+ $emit('fetchFileList')"
+ >
+
+
+ {{ $t('quotation.additionalFile') }}
+
+
+
+
+ $emit('upload', f as unknown as FileList)"
+ @close="(v) => $emit('remove', v)"
+ />
+
+
+
+
diff --git a/src/pages/09_task-order/expansion/DocumentExpansion.vue b/src/pages/09_task-order/expansion/DocumentExpansion.vue
new file mode 100644
index 00000000..d949fb7a
--- /dev/null
+++ b/src/pages/09_task-order/expansion/DocumentExpansion.vue
@@ -0,0 +1,108 @@
+
+
+
+
+
+ {{ $t('general.information', { msg: $t('general.document') }) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/09_task-order/expansion/PaymentExpansion.vue b/src/pages/09_task-order/expansion/PaymentExpansion.vue
new file mode 100644
index 00000000..79d1f295
--- /dev/null
+++ b/src/pages/09_task-order/expansion/PaymentExpansion.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+ {{ $t('general.payment') }}
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/09_task-order/expansion/ProductExpansion.vue b/src/pages/09_task-order/expansion/ProductExpansion.vue
new file mode 100644
index 00000000..32d69f8c
--- /dev/null
+++ b/src/pages/09_task-order/expansion/ProductExpansion.vue
@@ -0,0 +1,310 @@
+
+
+
+
+
+ {{ $t('general.information', { msg: $t('taskOrder.productList') }) }}
+
+
+
+
+
+
+
+
+
+ {{ col.label && $t(col.label) }}
+ {{ col.label === 'quotation.vat' ? '%' : '' }}
+
+
+
+
+
+
+
+
+ {{ props.rowIndex + 1 }}
+
+
+ {{ props.row.product.code }}
+
+
+
+
+
+
+
+
+
+ {{ props.row.product.name }}
+
+
+ {{ props.row.list.length }}
+
+
+ {{
+ formatNumberDecimal(
+ calcPricePerUnit(props.row.product) +
+ (props.row.product.calcVat
+ ? calcPricePerUnit(props.row.product) *
+ (config?.vat || 0.07)
+ : 0),
+ 2,
+ )
+ }}
+
+
+
+ {
+ if (typeof v === 'string')
+ discount4Show[props.rowIndex] = commaInput(v);
+ const x = parseFloat(
+ discount4Show[props.rowIndex] &&
+ typeof discount4Show[props.rowIndex] === 'string'
+ ? discount4Show[props.rowIndex].replace(/,/g, '')
+ : '',
+ );
+
+ const product = taskProduct.find(
+ (v) => v.productId === props.row.product.id,
+ );
+ if (product) {
+ product.discount = x;
+ }
+ }
+ "
+ />
+
+
+
+ {{ formatNumberDecimal(calcPricePerUnit(props.row.product), 2) }}
+
+
+
+ {{
+ formatNumberDecimal(
+ props.row.product.calcVat
+ ? precisionRound(
+ (calcPricePerUnit(props.row.product) *
+ props.row.list.length -
+ (taskProduct.find(
+ (v) => v.productId === props.row.product.id,
+ )?.discount || 0)) *
+ (config?.vat || 0.07),
+ )
+ : 0,
+ 2,
+ )
+ }}
+
+
+
+ {{
+ formatNumberDecimal(
+ calcPrice(props.row.product, props.row.list.length),
+ 2,
+ )
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ $t('general.numberOf', {
+ msg: $t('productService.product.product'),
+ })
+ }}
+
+
{{ taskList.length }}
+
+
+
+
+
diff --git a/src/pages/09_task-order/expansion/RemarkExpansion.vue b/src/pages/09_task-order/expansion/RemarkExpansion.vue
new file mode 100644
index 00000000..d33fcf73
--- /dev/null
+++ b/src/pages/09_task-order/expansion/RemarkExpansion.vue
@@ -0,0 +1,67 @@
+
+
+
+
+
+ {{ $t('general.remark') }}
+
+
+
+
+ {
+ remark = v;
+ }
+ "
+ />
+
+
+
+
diff --git a/src/pages/09_task-order/expansion/receive/InfoMessengerExpansion.vue b/src/pages/09_task-order/expansion/receive/InfoMessengerExpansion.vue
new file mode 100644
index 00000000..262bb99d
--- /dev/null
+++ b/src/pages/09_task-order/expansion/receive/InfoMessengerExpansion.vue
@@ -0,0 +1,149 @@
+
+
+
+
+
+ {{ $t('general.information', { msg: $t('personnel.MESSENGER') }) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ contactName }}
+
+
+
+
+
+
+
+
+
+ {{ receivedDate ? dateFormat(receivedDate) : '-' }}
+
+
+
+
+
+ {{ deliveryDate ? dateFormat(deliveryDate) : '-' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/09_task-order/expansion/receive/InfoProductExpansion.vue b/src/pages/09_task-order/expansion/receive/InfoProductExpansion.vue
new file mode 100644
index 00000000..98a895ff
--- /dev/null
+++ b/src/pages/09_task-order/expansion/receive/InfoProductExpansion.vue
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+ {{ $t('taskOrder.productList', { msg: $t('personnel.MESSENGER') }) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('productService.title') }}
+
+
+
+
+
+
+
diff --git a/src/pages/09_task-order/expansion/receive/TableProductExpantion.vue b/src/pages/09_task-order/expansion/receive/TableProductExpantion.vue
new file mode 100644
index 00000000..fce68b2e
--- /dev/null
+++ b/src/pages/09_task-order/expansion/receive/TableProductExpantion.vue
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+ ค่าบริการและค่าดำเนินงานยื่นขอบัญชีรายชื่อ (Name list)
+ สัญชาติเมียนมา
+
+
+
AC103
+
+
+
+
+
+
+
+
diff --git a/src/pages/09_task-order/form.ts b/src/pages/09_task-order/form.ts
new file mode 100644
index 00000000..1f568249
--- /dev/null
+++ b/src/pages/09_task-order/form.ts
@@ -0,0 +1,164 @@
+import { defineStore } from 'pinia';
+import { useI18n } from 'vue-i18n';
+import { ref } from 'vue';
+import { dialog } from 'stores/utils';
+
+// NOTE: Import types
+import {
+ SetTaskStatusPayload,
+ TaskOrder,
+ TaskOrderPayload,
+ TaskStatus,
+} from 'stores/task-order/types';
+import { RequestWork } from 'src/stores/request-list';
+
+// NOTE: Import stores
+import { useTaskOrderStore } from 'src/stores/task-order';
+
+export const useTaskOrderForm = defineStore('task-order-form', () => {
+ const { t } = useI18n();
+ const taskOrderStore = useTaskOrderStore();
+ const DEFAULT_DATA: TaskOrderPayload = {
+ taskList: [],
+ institutionId: '',
+ contactTel: '',
+ contactName: '',
+ taskName: '',
+ };
+
+ const state = ref<{
+ mode: null | 'info' | 'create' | 'edit';
+ setTaskStatusList: SetTaskStatusPayload[];
+ }>({
+ mode: null,
+ setTaskStatusList: [],
+ });
+
+ let resetFormData = structuredClone(DEFAULT_DATA);
+
+ const currentFormData = ref(structuredClone(resetFormData));
+ const fullTaskOrder = ref();
+
+ function resetForm(clean = false) {
+ if (clean) {
+ currentFormData.value = structuredClone(DEFAULT_DATA);
+ resetFormData = structuredClone(DEFAULT_DATA);
+ return;
+ }
+
+ currentFormData.value = structuredClone(resetFormData);
+
+ state.value.mode = 'info';
+ }
+
+ async function assignFormData(
+ id: string,
+ mode: 'info' | 'edit' | 'assign' = 'info',
+ userId?: string,
+ ) {
+ const res = await taskOrderStore.getTaskOrderById(id, {
+ taskAssignedUserId: userId,
+ });
+
+ if (!res) return;
+
+ fullTaskOrder.value = structuredClone(res);
+ resetFormData = Object.assign(res, {
+ requestWork: res.taskList.map((v) => ({
+ step: v.step,
+ requestWorkId: v.requestWorkId,
+ })),
+ });
+
+ currentFormData.value = structuredClone(resetFormData);
+
+ if (mode === 'assign') return;
+
+ state.value.mode = mode;
+ }
+
+ async function submitTask(opt?: {
+ taskProduct?: {
+ productId: string;
+ discount?: number;
+ }[];
+ }) {
+ let succeed = false;
+
+ if (state.value.mode === 'create') {
+ const mapTaskList = currentFormData.value.taskList.map((v) => ({
+ step: v.step,
+ requestWorkId: v.requestWorkId,
+ }));
+ const res = await taskOrderStore.createTaskOrder({
+ ...currentFormData.value,
+ taskProduct: opt?.taskProduct,
+ taskList: mapTaskList,
+ });
+
+ if (res) {
+ currentFormData.value.id = res.id;
+ succeed = true;
+ }
+ }
+
+ if (state.value.mode === 'edit' && !!currentFormData.value.id) {
+ const res = await taskOrderStore.editTaskOrder(currentFormData.value);
+ if (res) {
+ succeed = true;
+ }
+ }
+
+ return succeed;
+ }
+
+ async function changeStatus(
+ value: {
+ requestWorkId: string;
+ step: number;
+ failedComment?: string;
+ failedType?: string;
+ }[],
+ status: TaskStatus,
+ setRowStatus: () => void,
+ ) {
+ dialog({
+ color: 'warning',
+ icon: 'mdi-alert',
+ title: t('dialog.title.confirmChangeStatus'),
+ actionText: t('general.confirm'),
+ persistent: true,
+ message: t('dialog.message.confirmChangeStatus'),
+ action: async () => {
+ if (!currentFormData.value.id) return;
+
+ const records = value.map((item) => ({
+ failedComment: item.failedComment,
+ failedType: item.failedType,
+ taskStatus: status,
+ requestWorkId: item.requestWorkId,
+ step: item.step,
+ }));
+
+ const res = await taskOrderStore.changeTaskStatus(
+ currentFormData.value.id,
+ records,
+ );
+
+ if (res) setRowStatus();
+ },
+ cancel: () => {},
+ });
+ }
+
+ return {
+ currentFormData,
+ state,
+ fullTaskOrder,
+
+ resetForm,
+ assignFormData,
+ submitTask,
+ changeStatus,
+ };
+});
diff --git a/src/pages/09_task-order/order_view/MainPage.vue b/src/pages/09_task-order/order_view/MainPage.vue
new file mode 100644
index 00000000..a77ef621
--- /dev/null
+++ b/src/pages/09_task-order/order_view/MainPage.vue
@@ -0,0 +1,1088 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ view === TaskOrderStatus.Complete
+ ? $t('taskOrder.goodReceipt')
+ : $t('taskOrder.title')
+ }}
+
+
+ {{
+ $t('quotation.processOn', {
+ msg: dateFormatJS({
+ date: fullTaskOrder
+ ? fullTaskOrder.createdAt
+ : new Date(Date.now()),
+ dayStyle: 'numeric',
+ monthStyle: 'long',
+ locale: $i18n.locale === 'tha' ? 'th-Th' : 'en-US',
+ }),
+ })
+ }}
+ {{
+ dateFormat(
+ fullTaskOrder ? fullTaskOrder.createdAt : new Date(Date.now()),
+ false,
+ true,
+ )
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
{
+ submitForm();
+ }
+ "
+ >
+
+
+
+
+
+
+
{
+ if (!currentFormData.id) return;
+ getFileList(currentFormData.id);
+ }
+ "
+ @upload="
+ async (f) => {
+ if (!currentFormData.id) return;
+
+ fileList = f;
+
+ await uploadFile(currentFormData.id, f);
+ }
+ "
+ @remove="
+ async (n) => {
+ if (!currentFormData.id) return;
+ await remove(currentFormData.id, n);
+ }
+ "
+ />
+
+
+
+
+
+
+
+ {{ $t('menu.product') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ product.name }}
+
+ {{ product.code }}
+
+
+
+
+
+
+ {{ taskStatusCount(v, product.id) }}
+
+
+
+
+
+
+
+
+ {
+ openFailedDialog(
+ subProps.row,
+ subProps.rowIndex,
+ );
+ }
+ "
+ @change-status="
+ (status) =>
+ taskOrderFormStore.changeStatus(
+ [
+ {
+ requestWorkId: subProps.row.id || '',
+ step: subProps.row._template?.step || 0,
+ },
+ ],
+ status,
+ () => {
+ subProps.row.taskStatus = status;
+
+ if (fullTaskOrder) {
+ const target =
+ fullTaskOrder.taskList.find(
+ (t) =>
+ t.requestWorkId ===
+ subProps.row.id,
+ );
+ if (target) {
+ target.taskStatus = status;
+ }
+ }
+ },
+ )
+ "
+ />
+
+
+
+
+
+
{
+ pageState.failedDialog = false;
+ taskStatusList = [];
+ }
+ "
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ taskProduct = [];
+ const taskP = currentFormData.taskList
+ .map((v) => ({
+ productId:
+ v.requestWorkStep?.requestWork.productService.productId ?? '',
+ discount: 0,
+ }))
+ .filter(
+ (value, index, self) =>
+ index === self.findIndex((t) => t.productId === value.productId),
+ );
+ taskProduct = taskP;
+ }
+ "
+ />
+
+
diff --git a/src/pages/09_task-order/receive_view/FailRemarkDialog.vue b/src/pages/09_task-order/receive_view/FailRemarkDialog.vue
new file mode 100644
index 00000000..eb8869d9
--- /dev/null
+++ b/src/pages/09_task-order/receive_view/FailRemarkDialog.vue
@@ -0,0 +1,299 @@
+
+
+
+
+
+
+
+
+ taskStatusList.splice(v.index, 1)"
+ @add="
+ (v) => {
+ const index = taskStatusList.findIndex(
+ (item) => item.requestWorkId === v.value.id,
+ );
+ if (index !== -1) {
+ taskStatusList.splice(index, 1);
+ return;
+ }
+ taskStatusList.push({
+ code: `${v.value.productService.product.code}-${v.value.request.code}`,
+ requestWorkId: v.value.id,
+ step: v.value._template.step,
+ });
+ }
+ "
+ >
+
+
+ {{ taskStatusList[scope.index].code }}
+
+
+
+ {{ $t('productService.title') }}
+
+
+ {{
+ tooltip(scope.opt.requestWorkId)?.productService.product
+ .name
+ }}
+ ({{
+ tooltip(scope.opt.requestWorkId)?.productService.product
+ .code
+ }})
+
+
+
+ {{ $t('quotation.employeeName') }}
+
+
+ {{
+ getEmployeeName(tooltip(scope.opt.requestWorkId)?.request, {
+ locale: $i18n.locale,
+ })
+ }}
+ ({{ tooltip(scope.opt.requestWorkId)?.request.code }})
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('productService.title') }}:
+
+ {{ scope.opt.productService.product.name }}
+ ({{ scope.opt.productService.product.code }})
+
+
+
+
+ {{ $t('quotation.employeeName') }}:
+ {{
+ getEmployeeName(scope.opt.request, { locale: $i18n.locale })
+ }}
+ ({{ scope.opt.request.code }})
+
+
+
+
+
+
+
+
+
+
+
+ {{ opt.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/09_task-order/receive_view/MainPage.vue b/src/pages/09_task-order/receive_view/MainPage.vue
new file mode 100644
index 00000000..655f18bd
--- /dev/null
+++ b/src/pages/09_task-order/receive_view/MainPage.vue
@@ -0,0 +1,774 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('taskOrder.receive') }}
+
+
+ {{
+ $t('quotation.processOn', {
+ msg: dateFormatJS({
+ date: fullTaskOrder
+ ? fullTaskOrder.createdAt
+ : new Date(Date.now()),
+ dayStyle: 'numeric',
+ monthStyle: 'long',
+ locale: $i18n.locale === 'tha' ? 'th-Th' : 'en-US',
+ }),
+ })
+ }}
+ {{
+ dateFormat(
+ fullTaskOrder ? fullTaskOrder.createdAt : new Date(Date.now()),
+ false,
+ true,
+ )
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('taskOrder.allProduct') }}
+ {{ fullTaskOrder.taskList.length }}
+
+
+ {{ $t('taskOrder.alreadySentTask') }}
+
+ {{
+ fullTaskOrder.taskList.filter(
+ (t) =>
+ t.taskStatus === TaskStatus.Complete ||
+ t.taskStatus === TaskStatus.Success ||
+ t.taskStatus === TaskStatus.Validate ||
+ t.taskStatus === TaskStatus.Redo ||
+ t.taskStatus === TaskStatus.Failed,
+ ).length
+ }}
+
+
+
+ {{ $t('taskOrder.status.Pending') }}
+
+ {{
+ fullTaskOrder.taskList.filter(
+ (t) => t.taskStatus === TaskStatus.InProgress,
+ ).length
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ product.name }}
+
+ {{ product.code }}
+
+
+
+
+
+ {{ list.length }}
+
+
+
+
+
+
+ {{ taskStatusCount(v, product.id) }}
+
+
+
+
+
+
+ {{ $t('quotation.employeeList') }}
+
+
+
handleChangeStatus(v, i)"
+ v-model:selected-employee="selectedEmployee[i]"
+ >
+
+ {
+ taskStatusRecords = [
+ {
+ code: `${subProps.row.productService.product.code}-${subProps.row.request.code}`,
+ requestWorkId: subProps.row.id || '',
+ step: subProps.row._template?.step || 0,
+ failedComment:
+ fullTaskOrder?.taskList[subProps.rowIndex]
+ .failedComment || '',
+ failedType:
+ fullTaskOrder?.taskList[subProps.rowIndex]
+ .failedType || '',
+ },
+ ];
+ failedDialog = true;
+ }
+ "
+ @change-status="
+ (status) => {
+ handleChangeStatus(
+ {
+ data: [subProps.row],
+ status: status,
+ },
+ i,
+ );
+ }
+ "
+ />
+
+
+
+
+
+
+
{
+ await taskOrderFormStore.changeStatus(
+ taskStatusRecords,
+ TaskStatus.Failed,
+ () => {
+ if (!currentFormData.id) return;
+ taskOrderFormStore.assignFormData(
+ currentFormData.id,
+ 'info',
+ getUserId(),
+ );
+ },
+ );
+ selectedEmployee = [];
+ }
+ "
+ @close="
+ () => {
+ failedDialog = false;
+ taskStatusRecords = [];
+ }
+ "
+ />
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/09_task-order/receive_view/ReceiveDialog.vue b/src/pages/09_task-order/receive_view/ReceiveDialog.vue
new file mode 100644
index 00000000..d383a925
--- /dev/null
+++ b/src/pages/09_task-order/receive_view/ReceiveDialog.vue
@@ -0,0 +1,250 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/router/routes.ts b/src/router/routes.ts
index b2382b7f..6d9a44bc 100644
--- a/src/router/routes.ts
+++ b/src/router/routes.ts
@@ -128,6 +128,27 @@ const routes: RouteRecordRaw[] = [
name: 'requestListView',
component: () => import('pages/08_request-list/RequestListView.vue'),
},
+ {
+ path: '/task-order/order/add',
+ name: 'TaskOrderAdd',
+ component: () => import('pages/09_task-order/order_view/MainPage.vue'),
+ },
+ {
+ path: '/task-order/doc/:id',
+ name: 'TaskOrderAdd',
+ component: () => import('pages/09_task-order/document_view/MainPage.vue'),
+ },
+ {
+ path: '/task-order/order/:id',
+ name: 'TaskOrderView',
+ component: () => import('pages/09_task-order/order_view/MainPage.vue'),
+ },
+
+ {
+ path: '/task-order/receive/:id',
+ name: 'TaskReceiveView',
+ component: () => import('pages/09_task-order/receive_view/MainPage.vue'),
+ },
// Always leave this as last one,
// but you can also remove it
diff --git a/src/stores/customer/index.ts b/src/stores/customer/index.ts
index 1e7e1f05..132cd0c6 100644
--- a/src/stores/customer/index.ts
+++ b/src/stores/customer/index.ts
@@ -85,6 +85,7 @@ const useCustomerStore = defineStore('api-customer', () => {
query?: string;
page?: number;
pageSize?: number;
+ activeRegisBranchOnly?: boolean;
},
Data extends Pagination<
(CustomerBranch &
diff --git a/src/stores/institution/index.ts b/src/stores/institution/index.ts
index 4eb4fed7..930ebda2 100644
--- a/src/stores/institution/index.ts
+++ b/src/stores/institution/index.ts
@@ -21,15 +21,29 @@ export const useInstitution = defineStore('institution-store', () => {
return null;
}
- async function getInstitutionList(params?: {
+ async function getInstitutionList(opts?: {
page?: number;
pageSize?: number;
query?: string;
group?: string;
+ payload?: { group?: string[] };
}) {
- const res = await api.get>('/institution', {
- params,
- });
+ const { payload, ...params } = opts || {};
+
+ console.log(params.query);
+
+ const res = payload
+ ? await api.post>(
+ '/institution/list',
+ payload,
+ {
+ params,
+ },
+ )
+ : await api.get>(`/institution`, {
+ params,
+ });
+
if (res.status < 400) {
return res.data;
}
diff --git a/src/stores/product-service/types.ts b/src/stores/product-service/types.ts
index 3116de51..f07af36c 100644
--- a/src/stores/product-service/types.ts
+++ b/src/stores/product-service/types.ts
@@ -1,6 +1,9 @@
import { Status } from '../types';
import { UpdatedBy, CreatedBy } from 'stores/types';
-import { WorkFlowPayloadStep } from '../workflow-template/types';
+import {
+ WorkFlowPayloadStep,
+ WorkflowTemplate,
+} from '../workflow-template/types';
export interface TreeProduct {
name: string;
@@ -27,6 +30,8 @@ export interface Service {
work: Work[];
imageUrl: string;
registeredBranchId: string;
+ workflowId?: string;
+ workflow: WorkflowTemplate;
}
export interface WorkCreate {
@@ -80,6 +85,8 @@ export interface Attributes {
workflowStep: (WorkFlowPayloadStep & { productsId: string[] })[];
}
+export type PropVariant = PropString | PropNumber | PropDate | PropOptions;
+
export type PropString = {
type: 'string';
fieldName: string;
diff --git a/src/stores/quotations/types.ts b/src/stores/quotations/types.ts
index f5b7079d..53ca9c54 100644
--- a/src/stores/quotations/types.ts
+++ b/src/stores/quotations/types.ts
@@ -3,6 +3,7 @@ import { District, Province, SubDistrict } from '../address';
import { CreatedBy, Status, UpdatedBy } from '../types';
import { Invoice } from '../payment/types';
import { Employee } from '../employee/types';
+import { WorkflowTemplate } from '../workflow-template/types';
export type PayCondition =
| 'Full'
@@ -184,6 +185,7 @@ type ServiceRelation = {
createdByUserId: string;
updatedAt: string;
updatedByUserId: string;
+ workflow?: WorkflowTemplate;
work: (WorkRelation & {
productOnWork: {
@@ -331,6 +333,8 @@ export type QuotationFull = {
updatedByUserId: string;
updatedAt: string | Date;
updatedBy: UpdatedBy;
+
+ agentPrice?: boolean;
};
export type QuotationPayload = {
diff --git a/src/stores/request-list/index.ts b/src/stores/request-list/index.ts
index 8f317c18..73d44ca0 100644
--- a/src/stores/request-list/index.ts
+++ b/src/stores/request-list/index.ts
@@ -215,9 +215,12 @@ export const useRequestList = defineStore('request-list', () => {
}
async function getRequestWorkList(params?: {
+ requestDataId?: string;
+ query?: string;
page?: number;
pageSize?: number;
- requestDataId?: string;
+ workStatus?: RequestWorkStatus;
+ readyToTask?: boolean;
}) {
const res = await api.get>('/request-work', {
params,
@@ -290,3 +293,5 @@ export const useRequestList = defineStore('request-list', () => {
cancelRequest,
};
});
+
+export * from './types.ts';
diff --git a/src/stores/request-list/types.ts b/src/stores/request-list/types.ts
index 74b66bb4..6f8ddfe1 100644
--- a/src/stores/request-list/types.ts
+++ b/src/stores/request-list/types.ts
@@ -22,6 +22,7 @@ export enum RequestDataStatus {
Pending = 'Pending',
InProgress = 'InProgress',
Completed = 'Completed',
+ Canceled = 'Canceled',
}
export enum RequestWorkStatus {
diff --git a/src/stores/task-order/index.ts b/src/stores/task-order/index.ts
new file mode 100644
index 00000000..18bf19c8
--- /dev/null
+++ b/src/stores/task-order/index.ts
@@ -0,0 +1,189 @@
+import { ref } from 'vue';
+import { defineStore } from 'pinia';
+import { api } from 'src/boot/axios';
+
+// NOTE: Stores
+
+// NOTE: Type
+import {
+ SetTaskStatusPayload,
+ TaskOrder,
+ TaskOrderPayload,
+ TaskOrderStatus,
+ UserTaskStatus,
+} from './types';
+import { PaginationResult } from 'src/types';
+import { manageAttachment } from '../utils';
+
+export const useTaskOrderStore = defineStore('taskorder-store', () => {
+ const data = ref([]);
+ const page = ref(1);
+ const pageMax = ref(1);
+ const pageSize = ref(30);
+ const stats = ref>({
+ [TaskOrderStatus.Pending]: 0,
+ [TaskOrderStatus.InProgress]: 0,
+ [TaskOrderStatus.Validate]: 0,
+ [TaskOrderStatus.Complete]: 0,
+ [TaskOrderStatus.Accept]: 0,
+ [TaskOrderStatus.Submit]: 0,
+ });
+ const fileManager = manageAttachment(api, 'task-order');
+
+ async function getTaskOrderStats() {
+ const res =
+ await api.get>('/task-order/stats');
+
+ if (res.status < 400) {
+ stats.value = res.data;
+ return res.data;
+ }
+ return null;
+ }
+
+ async function getTaskOrderList(params?: {
+ page?: number;
+ pageSize?: number;
+ query?: string;
+ taskOrderStatus?: TaskOrderStatus;
+ assignedUserId?: boolean;
+ }) {
+ const res = await api.get>('/task-order', {
+ params,
+ });
+
+ if (res.status < 400) {
+ return res.data;
+ }
+ return null;
+ }
+
+ async function getTaskOrderById(
+ id: string,
+ params?: { taskAssignedUserId?: string },
+ ) {
+ const res = await api.get(`/task-order/${id}`, { params });
+
+ if (res.status < 400) {
+ return res.data;
+ }
+ return null;
+ }
+
+ async function createTaskOrder(body: TaskOrderPayload) {
+ const res = await api.post('/task-order', body);
+
+ if (res.status < 400) {
+ return res.data;
+ }
+ return null;
+ }
+
+ async function changeTaskStatus(id: string, body: SetTaskStatusPayload[]) {
+ const res = await api.post(
+ `/task-order/${id}/set-task-status`,
+ body,
+ );
+
+ if (res.status < 400) {
+ return res.data;
+ }
+ return null;
+ }
+
+ async function acceptTaskOrder(id: string | string[]) {
+ const res = await api.post('/user-task-order/accept', {
+ taskOrderId: Array.isArray(id) ? id : [id],
+ });
+
+ if (res.status < 400) {
+ return res.data;
+ }
+ return null;
+ }
+
+ async function editTaskOrder(body: TaskOrderPayload) {
+ const res = await api.put(`/task-order/${body.id}`, {
+ taskList: body.taskList.map((v) => ({
+ step: v.step,
+ requestWorkId: v.requestWorkId,
+ })),
+ institutionId: body.institutionId,
+ contactTel: body.contactTel,
+ contactName: body.contactName,
+ taskStatus: body.taskStatus,
+ taskName: body.taskName,
+ });
+
+ if (res.status < 400) {
+ return res.data;
+ }
+ return null;
+ }
+
+ async function deleteTaskOrder(id: string) {
+ const res = await api.delete(`/task-order/${id}`);
+
+ if (res.status < 400) {
+ return true;
+ }
+ return null;
+ }
+
+ async function submitTaskOrder(id: string) {
+ const res = await api.post(`/task-order/${id}/submit`);
+
+ if (res.status < 400) {
+ return true;
+ }
+ return null;
+ }
+
+ async function completeTaskOrder(id: string) {
+ const res = await api.post(`/task-order/${id}/complete`);
+
+ if (res.status < 400) {
+ return true;
+ }
+ return null;
+ }
+
+ async function getUserTaskOrderList(params?: {
+ page?: number;
+ pageSize?: number;
+ query?: string;
+ userTaskStatus?: UserTaskStatus;
+ }) {
+ const res = await api.get>('/user-task-order', {
+ params,
+ });
+ if (res.status < 400) {
+ return res.data;
+ }
+ return null;
+ }
+
+ return {
+ data,
+ page,
+ pageMax,
+ pageSize,
+ stats,
+
+ getTaskOrderStats,
+ getTaskOrderList,
+ getTaskOrderById,
+ createTaskOrder,
+ acceptTaskOrder,
+ editTaskOrder,
+ deleteTaskOrder,
+ changeTaskStatus,
+ submitTaskOrder,
+ completeTaskOrder,
+
+ // for receive user (messenger role)
+ getUserTaskOrderList,
+
+ ...fileManager,
+ };
+});
diff --git a/src/stores/task-order/types.ts b/src/stores/task-order/types.ts
new file mode 100644
index 00000000..5c00befb
--- /dev/null
+++ b/src/stores/task-order/types.ts
@@ -0,0 +1,207 @@
+import { strict } from 'node:assert';
+import { Branch } from '../branch/types';
+import { Product, Service, Work } from '../product-service/types';
+import { RequestWork } from '../request-list/types';
+import { CreatedBy } from '../types';
+import { User } from '../user';
+
+export enum TaskOrderStatus {
+ Pending = 'Pending',
+ InProgress = 'InProgress',
+ Validate = 'Validate',
+ Complete = 'Complete',
+ Accept = 'Accept', // messenger only
+ Submit = 'Submit', // messenger only
+}
+
+export enum TaskStatus {
+ Pending = 'Pending',
+ InProgress = 'InProgress',
+ Success = 'Success',
+ Failed = 'Failed',
+ Redo = 'Redo',
+ Validate = 'Validate',
+ Complete = 'Complete',
+ Canceled = 'Canceled',
+}
+
+export interface TaskOrder {
+ createdBy: CreatedBy;
+ registeredBranch: Branch;
+ taskProduct: {
+ productId: string;
+ discount?: number;
+ }[];
+ taskList: {
+ step: number;
+ requestWorkId: string;
+ requestWorkStep: Task;
+ taskStatus: TaskStatus;
+ taskOrderId: string;
+ id: string;
+ failedType?: string;
+ failedComment?: string;
+ }[];
+ institution: Institution;
+ createdByUserId: string;
+ createdAt: string;
+ registeredBranchId: string;
+ institutionId: string;
+ contactTel: string;
+ contactName: string;
+ taskOrderStatus: TaskOrderStatus;
+ taskName: string;
+ code: string;
+ id: string;
+ userTask: UserTask[];
+}
+
+export interface UserTask {
+ id: string;
+ taskOrderId: string;
+ userId: string;
+ userTaskStatus: UserTaskStatus;
+}
+
+export interface Institution {
+ selectedImage: string;
+ subDistrictId: string;
+ districtId: string;
+ provinceId: string;
+ streetEN: string;
+ street: string;
+ mooEN: string;
+ moo: string;
+ soiEN: string;
+ soi: string;
+ addressEN: string;
+ address: string;
+ nameEN: string;
+ group: string;
+ name: string;
+ code: string;
+ id: string;
+}
+
+export interface AcceptedBy {
+ updatedByUserId: string;
+ updatedAt: string;
+ statusOrder: number;
+ status: string;
+ birthDate: string;
+ trainingPlace: string;
+ importNationality: string;
+ sourceNationality: string;
+ licenseExpireDate: string;
+ licenseIssueDate: string;
+ licenseNo: string;
+ discountCondition: string;
+ citizenExpire: string;
+ citizenIssue: string;
+ citizenId: string;
+ userRole: string;
+ userType: string;
+ checkpointEN: string;
+ checkpoint: string;
+ retireDate: string;
+ startDate: string;
+ registrationNo: string;
+ telephoneNo: string;
+ email: string;
+ gender: string;
+ username: string;
+ lastNameEN: string;
+ lastName: string;
+ middleNameEN: string;
+ middleName: string;
+ firstNameEN: string;
+ firstName: string;
+ namePrefix: string;
+ selectedImage: string;
+ subDistrictId: string;
+ districtId: string;
+ provinceId: string;
+ streetEN: string;
+ street: string;
+ mooEN: string;
+ moo: string;
+ soiEN: string;
+ soi: string;
+ addressEN: string;
+ address: string;
+ createdByUserId: string;
+ createdAt: string;
+ code: string;
+ id: string;
+}
+
+export interface Task {
+ step: number;
+ workStatus: string;
+ requestWorkId: string;
+ attributes: any;
+ customerDuty?: boolean;
+ customerDutyCost?: number;
+ companyDuty?: boolean;
+ companyDutyCost?: number;
+ individualDuty?: boolean;
+ individualDutyCost?: number;
+ responsibleUserLocal?: boolean;
+ responsibleUserId?: string;
+ responsibleUser?: User;
+ taskOrderId: string;
+ requestWork: RequestWork;
+}
+
+export interface TaskOrderPayload {
+ taskList: {
+ step: number;
+ requestWorkId: string;
+ requestWorkStep?: Task;
+ taskStatus?: TaskStatus;
+ }[];
+ taskProduct?: {
+ productId: string;
+ discount?: number;
+ }[];
+ institutionId: string;
+ contactTel: string;
+ contactName: string;
+ taskStatus?: TaskOrderStatus;
+ taskName: string;
+ registeredBranchId?: string;
+ id?: string;
+ code?: string;
+}
+
+export interface ProductService {
+ id: string;
+ quotationId: string;
+ order: number;
+ vat: number;
+ amount: number;
+ discount: number;
+ pricePerUnit: number;
+ installmentNo: number;
+ productId: string;
+ workId: string;
+ serviceId: string;
+ attributes: any;
+ product: Product;
+ service: Service;
+ work: Work;
+}
+
+export enum UserTaskStatus {
+ Pending = 'Pending',
+ Accept = 'Accept',
+ Submit = 'Submit',
+}
+
+export interface SetTaskStatusPayload {
+ failedComment?: string;
+ failedType?: string;
+ taskStatus: TaskStatus;
+ requestWorkId: string;
+ step: number;
+}
diff --git a/src/stores/utils/index.ts b/src/stores/utils/index.ts
index 8120e62e..e979692a 100644
--- a/src/stores/utils/index.ts
+++ b/src/stores/utils/index.ts
@@ -117,7 +117,7 @@ export function deleteItem(items: unknown[], index: number) {
}
}
-export function formatNumberDecimal(num: number, point: number): string {
+export function formatNumberDecimal(num: number, point: number = 2): string {
return num.toLocaleString('eng', {
minimumFractionDigits: point,
maximumFractionDigits: point,
diff --git a/src/stores/workflow-template/types.ts b/src/stores/workflow-template/types.ts
index bac09ebe..8135a44e 100644
--- a/src/stores/workflow-template/types.ts
+++ b/src/stores/workflow-template/types.ts
@@ -21,7 +21,7 @@ export type WorkflowStep = {
userId: string;
user: CreatedBy;
}[];
- responsibleInstitution: string[];
+ responsibleInstitution: (string | { group: string })[];
attributes: WorkFlowAttributes;
};
diff --git a/src/utils/address.ts b/src/utils/address.ts
index d239adea..a66ac185 100644
--- a/src/utils/address.ts
+++ b/src/utils/address.ts
@@ -10,9 +10,9 @@ export function formatAddress(opt: {
soiEN?: string;
street?: string;
streetEN?: string;
- province: Province;
- district: District;
- subDistrict: SubDistrict;
+ province?: Province | null;
+ district?: District | null;
+ subDistrict?: SubDistrict | null;
en?: boolean;
}) {
const { t } = useI18n();
@@ -24,23 +24,29 @@ export function formatAddress(opt: {
if (opt.soiEN) addressParts.push(`Soi ${opt.soiEN},`);
if (opt.streetEN) addressParts.push(`${opt.streetEN} Rd.`);
- addressParts.push(`${opt.subDistrict.nameEN} sub-district,`);
- addressParts.push(`${opt.district.nameEN} district,`);
- addressParts.push(`${opt.province.nameEN},`);
+ if (opt.subDistrict) {
+ addressParts.push(`${opt.subDistrict.nameEN} sub-district,`);
+ }
+ if (opt.district) addressParts.push(`${opt.district.nameEN} district,`);
+ if (opt.province) addressParts.push(`${opt.province.nameEN},`);
} else {
// th
addressParts = [`${opt.address},`];
if (opt.moo) addressParts.push(`หมู่ ${opt.moo},`);
- if (opt.soi) addressParts.push(`ซอย${opt.soi},`);
+ if (opt.soi) addressParts.push(`ซอย ${opt.soi},`);
if (opt.street) addressParts.push(`ถนน${opt.street},`);
- addressParts.push(
- `${opt.province.id === '10' ? 'แขวง' : 'ตำบล'}${opt.subDistrict.name},`,
- );
- addressParts.push(
- `${opt.province.id === '10' ? 'เขต' : 'อำเภอ'}${opt.district.name},`,
- );
- addressParts.push(`จังหวัด${opt.province.name},`);
+ if (opt.subDistrict) {
+ addressParts.push(
+ `${opt.province?.id === '10' ? 'แขวง' : 'ตำบล'}${opt.subDistrict.name},`,
+ );
+ }
+ if (opt.district) {
+ addressParts.push(
+ `${opt.province?.id === '10' ? 'เขต' : 'อำเภอ'}${opt.district.name},`,
+ );
+ }
+ if (opt.province) addressParts.push(`จังหวัด${opt.province.name},`);
// addressParts.push(
// `${opt.province.id === '10' ? t('address.subArea') : t('address.subDistrict')}${opt.subDistrict.name},`,
// );
@@ -52,7 +58,7 @@ export function formatAddress(opt: {
// );
}
- addressParts.push(`${opt.subDistrict.zipCode}`);
+ if (opt.subDistrict) addressParts.push(`${opt.subDistrict.zipCode}`);
return addressParts.join(' ');
}