Compare commits
50 commits
version-0.
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65dcd138db | ||
| e6d06b39da | |||
| 3cab6cc0e5 | |||
|
|
9994366c74 | ||
|
|
f4db5ad855 | ||
|
|
15a812b50e | ||
|
|
f7a8416e7a | ||
|
|
79d6482caa | ||
|
|
75d5c7dfe8 | ||
|
|
637eeab3c2 | ||
| 2b1e3b12a4 | |||
|
|
a1ed625d32 | ||
|
|
59a3f964c4 | ||
|
|
2afb5ea7e9 | ||
| aaf776639d | |||
| 04c463a717 | |||
| 8e65a1c5a2 | |||
| 21fc2d5d96 | |||
| 73f43c2a29 | |||
| 16ea66484d | |||
| 90f31a0c87 | |||
| d1785faed2 | |||
| f68e8cf675 | |||
| cd4b087fec | |||
|
|
8a0340f588 | ||
| 2b9c8aa613 | |||
|
|
eebd585554 | ||
|
|
db5262da42 | ||
|
|
72b0e89642 | ||
|
|
2320883cb6 | ||
|
|
0e6bee7b62 | ||
| 5c867a496d | |||
|
|
d06c26c3c8 | ||
|
|
c4f088c5cb | ||
| 6cf8cf28aa | |||
|
|
1249f67a0f | ||
|
|
05d38b1ab3 | ||
|
|
d09484a52a | ||
|
|
d8d02a679d | ||
|
|
11047e569d | ||
|
|
f3b5b25bf3 | ||
|
|
e817e8fd05 | ||
|
|
18e5517325 | ||
|
|
5e13864d4a | ||
| 61dca12e5a | |||
| aa908f0c3d | |||
| 80056f8e0b | |||
|
|
02bb682150 | ||
| d2acd6ba4c | |||
| d53eb15a88 |
60 changed files with 1022 additions and 394 deletions
|
|
@ -293,15 +293,11 @@ watch(
|
|||
:readonly="readonly"
|
||||
:label="$t('form.birthDate')"
|
||||
:disabled-dates="disabledAfterToday"
|
||||
:rules="
|
||||
employee
|
||||
? []
|
||||
: [
|
||||
(val: string) =>
|
||||
!!val ||
|
||||
$t('form.error.selectField', { field: $t('form.birthDate') }),
|
||||
]
|
||||
"
|
||||
:rules="[
|
||||
(val: string) =>
|
||||
!!val ||
|
||||
$t('form.error.selectField', { field: $t('form.birthDate') }),
|
||||
]"
|
||||
/>
|
||||
|
||||
<q-input
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
<script setup lang="ts">
|
||||
import { CustomerBranch } from 'src/stores/customer';
|
||||
|
||||
import SelectCustomer from '../shared/select/SelectCustomer.vue';
|
||||
import SelectBranch from '../shared/select/SelectBranch.vue';
|
||||
|
||||
import { CustomerBranch } from 'src/stores/customer';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const branchId = defineModel<string>('branchId');
|
||||
const customerBranchId = defineModel<string>('customerBranchId');
|
||||
const agentPrice = defineModel<boolean>('agentPrice');
|
||||
|
|
@ -73,6 +74,7 @@ defineEmits<{
|
|||
required
|
||||
:readonly
|
||||
/>
|
||||
|
||||
<SelectCustomer
|
||||
id="about-select-customer-branch-id"
|
||||
for="about-select-customer-branch-id"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts" setup>
|
||||
import { QTableProps } from 'quasar';
|
||||
import { dateFormat } from 'src/utils/datetime';
|
||||
import { dateFormat, dateFormatJS } from 'src/utils/datetime';
|
||||
|
||||
import { formatNumberDecimal } from 'stores/utils';
|
||||
|
||||
|
|
@ -86,11 +86,11 @@ defineEmits<{
|
|||
</q-td>
|
||||
|
||||
<q-td v-if="visibleColumns.includes('createdAt')">
|
||||
{{ dateFormat(props.row.createdAt) }}
|
||||
{{ dateFormatJS({ date: props.row.createdAt }) }}
|
||||
</q-td>
|
||||
|
||||
<q-td v-if="visibleColumns.includes('dueDate')">
|
||||
{{ dateFormat(props.row.dueDate) }}
|
||||
{{ dateFormatJS({ date: props.row.dueDate }) }}
|
||||
</q-td>
|
||||
|
||||
<q-td v-if="visibleColumns.includes('contactName')">
|
||||
|
|
|
|||
|
|
@ -27,26 +27,38 @@ withDefaults(
|
|||
class="app-text-muted q-pr-sm"
|
||||
:width="iconSize || '2rem'"
|
||||
/>
|
||||
<span class="row col">
|
||||
<span class="col-12 app-text-muted-2" style="font-size: 10px">
|
||||
<span :id="`dd-wrapper-${label}`" class="row col">
|
||||
<span
|
||||
:id="`dd-label-${label}`"
|
||||
class="col-12 app-text-muted-2"
|
||||
style="font-size: 10px"
|
||||
>
|
||||
{{ label }}
|
||||
</span>
|
||||
<span class="col-12 ellipsis">
|
||||
<span :id="`dd-value-wrapper-${label}`" class="col-12 ellipsis">
|
||||
<slot name="value">
|
||||
<span
|
||||
:class="{ 'link cursor-pointer': clickable }"
|
||||
v-if="typeof value === 'string'"
|
||||
@click="clickable ? $emit('labelClick', value, null) : undefined"
|
||||
:id="`link-${value}`"
|
||||
:for="`link-${value}`"
|
||||
>
|
||||
{{ value }}
|
||||
<q-tooltip v-if="tooltip" :delay="500">{{ value }}</q-tooltip>
|
||||
</span>
|
||||
<span v-else :class="{ 'link cursor-pointer': clickable }">
|
||||
<span
|
||||
:id="`dd-value-${label}`"
|
||||
v-else
|
||||
:class="{ 'link cursor-pointer': clickable }"
|
||||
>
|
||||
<span
|
||||
v-for="(item, index) in value"
|
||||
:key="index"
|
||||
@click="$emit('labelClick', item, index)"
|
||||
class="link cursor-pointer"
|
||||
:id="`link-${item}`"
|
||||
:for="`link-${item}`"
|
||||
>
|
||||
{{ item }}
|
||||
<span v-if="index < value.length - 1">, </span>
|
||||
|
|
|
|||
|
|
@ -96,7 +96,9 @@ defineEmits<{
|
|||
expandedTree[expandedTree.length - 1] === node.id,
|
||||
}"
|
||||
>
|
||||
{{ node.name }}
|
||||
{{
|
||||
$i18n.locale === 'eng' ? node.nameEN || node.name : node.name
|
||||
}}
|
||||
</span>
|
||||
<span class="app-text-muted text-caption ellipsis">
|
||||
{{ node.code }}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ defineEmits<{
|
|||
(e: 'click', v: MouseEvent): void;
|
||||
}>();
|
||||
defineProps<{
|
||||
id?: string;
|
||||
icon?: string;
|
||||
color: string;
|
||||
iconOnly?: boolean;
|
||||
|
|
@ -18,6 +19,7 @@ defineProps<{
|
|||
|
||||
<template>
|
||||
<button
|
||||
:id="id"
|
||||
@click="(e) => $emit('click', e)"
|
||||
class="main-btn"
|
||||
:class="{
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ defineProps<{
|
|||
outlined?: boolean;
|
||||
disabled?: boolean;
|
||||
dark?: boolean;
|
||||
color?: string;
|
||||
|
||||
label?: string;
|
||||
icon?: string;
|
||||
|
|
@ -23,7 +24,7 @@ defineProps<{
|
|||
@click="(e) => $emit('click', e)"
|
||||
v-bind="{ ...$props, ...$attrs }"
|
||||
:icon="icon || 'mdi-content-save-outline'"
|
||||
color="207 96% 32%"
|
||||
:color="color || '207 96% 32%'"
|
||||
:title="iconOnly ? $t('general.save') : undefined"
|
||||
>
|
||||
{{ label || $t('general.save') }}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ let defaultFilter: (
|
|||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
prefix?: string;
|
||||
id?: string;
|
||||
label?: string;
|
||||
option: T[];
|
||||
|
|
@ -71,6 +72,7 @@ watch(
|
|||
</script>
|
||||
<template>
|
||||
<q-select
|
||||
:id="id"
|
||||
:placeholder="placeholder"
|
||||
outlined
|
||||
:clearable
|
||||
|
|
|
|||
|
|
@ -75,9 +75,9 @@ function setDefaultValue() {
|
|||
</script>
|
||||
<template>
|
||||
<SelectInput
|
||||
for="select-hq-id"
|
||||
v-model="value"
|
||||
incremental
|
||||
id="select-hq-id"
|
||||
:label
|
||||
:placeholder
|
||||
:readonly
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ defineEmits<{
|
|||
|
||||
type ExclusiveProps = {
|
||||
selectFirstValue?: boolean;
|
||||
prefix?: string;
|
||||
};
|
||||
|
||||
const props = defineProps<SelectProps<typeof getList> & ExclusiveProps>();
|
||||
|
|
@ -71,6 +72,7 @@ function setDefaultValue() {
|
|||
<SelectInput
|
||||
v-model="value"
|
||||
incremental
|
||||
:id="`${prefix || 'nome'}-select-user`"
|
||||
:label
|
||||
:placeholder
|
||||
:readonly
|
||||
|
|
|
|||
|
|
@ -145,6 +145,7 @@ function selectedIndex(item: Employee) {
|
|||
<template v-if="col.name === '#check'">
|
||||
<q-checkbox
|
||||
id="select-worker-all"
|
||||
for="select-worker-all"
|
||||
v-model="props.selected"
|
||||
@update:model-value="(v) => handleUpdate()"
|
||||
size="sm"
|
||||
|
|
@ -200,6 +201,7 @@ function selectedIndex(item: Employee) {
|
|||
v-model="props.selected"
|
||||
size="sm"
|
||||
:id="`select-worker-${props.row.firstName}`"
|
||||
:for="`select-worker-${props.row.firstName}`"
|
||||
/>
|
||||
</template>
|
||||
</q-td>
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ export default {
|
|||
documentStatus: 'Document Status',
|
||||
advanceSearch: 'Advance Search',
|
||||
totalPeople: '{meg} people',
|
||||
price: 'Price {price} Baht',
|
||||
},
|
||||
|
||||
menu: {
|
||||
|
|
@ -1233,6 +1234,9 @@ export default {
|
|||
taskListNotPending: 'One or more task is not pending.',
|
||||
reqNotMet: 'Not Match',
|
||||
systemError: 'A system error occurred.',
|
||||
taskOrderInvalid: 'Please select the product and the organization.',
|
||||
|
||||
flowAccountProductIdNotFound: 'Product not found in flow account',
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ export default {
|
|||
documentStatus: 'สถานะเอกสาร',
|
||||
advanceSearch: 'ค้นหาขั้นสูง',
|
||||
totalPeople: '{meg} คน',
|
||||
price: 'ราคา {price} บาท',
|
||||
},
|
||||
|
||||
menu: {
|
||||
|
|
@ -1219,6 +1220,8 @@ export default {
|
|||
'มีงานหนึ่งงานหรือมากกว่าที่ไม่อยู่ในสถานะรอดำเนินการ',
|
||||
reqNotMet: 'ไม่ตรงกัน',
|
||||
systemError: 'ระบบเกิดข้อผิดพลาด',
|
||||
taskOrderInvalid: 'โปรดเลือก สินค้าเเละสาขา',
|
||||
flowAccountProductIdNotFound: 'ไม่พบสินค้าใน flow account',
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { ref, onMounted, computed, reactive } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { getUserId, getUsername, logout, getRole } from 'src/services/keycloak';
|
||||
import { getUserId, getUsername, getName, logout, getRole } from 'src/services/keycloak';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import moment from 'moment';
|
||||
|
|
@ -39,7 +39,7 @@ const configStore = useConfigStore();
|
|||
const { data: notificationData } = storeToRefs(notificationStore);
|
||||
|
||||
const { visible } = storeToRefs(loaderStore);
|
||||
const { t } = useI18n({ useScope: 'global' });
|
||||
const { t, locale } = useI18n({ useScope: 'global' });
|
||||
const userStore = useUserStore();
|
||||
|
||||
const canvasModal = ref(false);
|
||||
|
|
@ -52,8 +52,14 @@ const unread = computed<number>(
|
|||
);
|
||||
const userImage = ref<string>();
|
||||
const userGender = ref('');
|
||||
const userName = ref({ th: '', en: '' });
|
||||
const canvasRef = ref();
|
||||
|
||||
const displayName = computed(() => {
|
||||
if (!userName.value.th && !userName.value.en) return getName() || 'Guest';
|
||||
return locale.value === 'eng' ? userName.value.en : userName.value.th;
|
||||
});
|
||||
|
||||
const language: {
|
||||
value: Lang;
|
||||
label: string;
|
||||
|
|
@ -161,9 +167,14 @@ onMounted(async () => {
|
|||
if (user === 'admin') return;
|
||||
if (uid) {
|
||||
const res = await userStore.fetchById(uid);
|
||||
if (res && res.gender) {
|
||||
userGender.value = res.gender;
|
||||
userImage.value = `${baseUrl}/user/${uid}/profile-image/${res.selectedImage}`;
|
||||
if (res) {
|
||||
if (res.gender) {
|
||||
userGender.value = res.gender;
|
||||
userImage.value = `${baseUrl}/user/${uid}/profile-image/${res.selectedImage}`;
|
||||
}
|
||||
// เก็บชื่อทั้งสองภาษา
|
||||
userName.value.th = `${res.firstName || ''} ${res.lastName || ''}`.trim();
|
||||
userName.value.en = `${res.firstNameEN || ''} ${res.lastNameEN || ''}`.trim();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -484,6 +495,7 @@ onMounted(async () => {
|
|||
<!-- User -->
|
||||
<ProfileMenu
|
||||
id="btn-profile-menu"
|
||||
:user-name="displayName"
|
||||
@logout="doLogout"
|
||||
@edit-personal-info="console.log('edit')"
|
||||
@signature="
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ const filterRole = ref<string[]>();
|
|||
defineProps<{
|
||||
userImage?: string;
|
||||
gender?: string;
|
||||
userName?: string;
|
||||
}>();
|
||||
|
||||
const inputFile = document.createElement('input');
|
||||
|
|
@ -147,9 +148,9 @@ onMounted(async () => {
|
|||
class="text-weight-bold ellipsis"
|
||||
style="max-width: 9vw"
|
||||
>
|
||||
{{ getName() }}
|
||||
{{ userName || getName() }}
|
||||
<q-tooltip>
|
||||
{{ getName() }}
|
||||
{{ userName || getName() }}
|
||||
</q-tooltip>
|
||||
</span>
|
||||
<span
|
||||
|
|
@ -234,12 +235,12 @@ onMounted(async () => {
|
|||
style="margin-top: 58px"
|
||||
>
|
||||
<span v-if="isLoggedIn()">
|
||||
{{ getName() }}
|
||||
{{ userName || getName() }}
|
||||
</span>
|
||||
<span v-else>{{ 'Guest' }}</span>
|
||||
<q-tooltip>
|
||||
<span v-if="isLoggedIn()">
|
||||
{{ getName() }}
|
||||
{{ userName || getName() }}
|
||||
</span>
|
||||
<span v-else>{{ 'Guest' }}</span>
|
||||
</q-tooltip>
|
||||
|
|
|
|||
|
|
@ -541,7 +541,8 @@ onMounted(async () => {
|
|||
<div class="col">
|
||||
<div class="col">
|
||||
{{
|
||||
props.row.customerType === 'CORP'
|
||||
props.row.customerType === 'CORP' &&
|
||||
locale === 'tha'
|
||||
? props.row.branch[0]?.registerName || '-'
|
||||
: optionStore.mapOption(
|
||||
props.row.branch[0].namePrefix,
|
||||
|
|
@ -551,10 +552,24 @@ onMounted(async () => {
|
|||
' ' +
|
||||
props.row.branch[0]?.lastName || '-'
|
||||
}}
|
||||
|
||||
{{
|
||||
props.row.customerType === 'CORP' &&
|
||||
locale === 'eng'
|
||||
? props.row.branch[0]?.registerNameEN || '-'
|
||||
: optionStore.mapOption(
|
||||
props.row.branch[0].namePrefix,
|
||||
) +
|
||||
' ' +
|
||||
props.row.branch[0]?.firstNameEN +
|
||||
' ' +
|
||||
props.row.branch[0]?.lastNameEN || '-'
|
||||
}}
|
||||
</div>
|
||||
<div class="col app-text-muted">
|
||||
{{
|
||||
props.row.customerType === 'CORP'
|
||||
props.row.customerType === 'CORP' &&
|
||||
locale === 'tha'
|
||||
? props.row.branch[0]?.registerNameEN || '-'
|
||||
: capitalizeFirstLetter(
|
||||
props.row.branch[0].namePrefix,
|
||||
|
|
@ -564,6 +579,19 @@ onMounted(async () => {
|
|||
' ' +
|
||||
props.row.branch[0]?.lastNameEN || '-'
|
||||
}}
|
||||
|
||||
{{
|
||||
props.row.customerType === 'CORP' &&
|
||||
locale === 'eng'
|
||||
? props.row.branch[0]?.registerName || '-'
|
||||
: capitalizeFirstLetter(
|
||||
props.row.branch[0].namePrefix,
|
||||
) +
|
||||
' ' +
|
||||
props.row.branch[0]?.firstName +
|
||||
' ' +
|
||||
props.row.branch[0]?.lastName || '-'
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -102,6 +102,8 @@ const {
|
|||
deleteWork,
|
||||
|
||||
importProduct,
|
||||
|
||||
productExport,
|
||||
} = productServiceStore;
|
||||
|
||||
const { workNameItems } = storeToRefs(productServiceStore);
|
||||
|
|
@ -1169,6 +1171,7 @@ function clearFormService() {
|
|||
profileSubmit.value = false;
|
||||
imageProduct.value = undefined;
|
||||
profileFileImg.value = null;
|
||||
serviceTab.value = 1;
|
||||
}
|
||||
|
||||
function sameFormService() {
|
||||
|
|
@ -1896,6 +1899,25 @@ async function submitWorkName(
|
|||
else await editWork(workId, data);
|
||||
}
|
||||
|
||||
async function triggerExport() {
|
||||
productExport({
|
||||
pageSize: 100_000,
|
||||
productGroupId: currentIdGroup.value,
|
||||
query: !!inputSearchProductAndService.value
|
||||
? inputSearchProductAndService.value
|
||||
: undefined,
|
||||
status:
|
||||
currentStatus.value === 'INACTIVE'
|
||||
? 'INACTIVE'
|
||||
: currentStatus.value === 'ACTIVE'
|
||||
? 'ACTIVE'
|
||||
: undefined,
|
||||
|
||||
startDate: searchDate.value[0] ? new Date(searchDate.value[0]) : undefined,
|
||||
endDate: searchDate.value[1] ? new Date(searchDate.value[1]) : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => formService.value.attributes.workflowId,
|
||||
async (a, b) => {
|
||||
|
|
@ -2229,7 +2251,6 @@ watch(
|
|||
</AdvanceSearch>
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<div class="row col-md-6" style="white-space: nowrap">
|
||||
<q-select
|
||||
v-show="$q.screen.gt.sm"
|
||||
|
|
@ -2780,6 +2801,13 @@ watch(
|
|||
}
|
||||
"
|
||||
/>
|
||||
|
||||
<SaveButton
|
||||
icon-only
|
||||
:icon="'material-symbols:download'"
|
||||
color="var(--info-bg)"
|
||||
@click.stop="triggerExport()"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="row col-md-6" style="white-space: nowrap">
|
||||
|
|
@ -4357,6 +4385,7 @@ watch(
|
|||
|
||||
<!-- add service -->
|
||||
<DialogForm
|
||||
v-if="dialogService"
|
||||
hide-footer
|
||||
no-address
|
||||
no-app-box
|
||||
|
|
@ -4722,6 +4751,7 @@ watch(
|
|||
<!-- edit service edit package-->
|
||||
<!-- :edit="!(formDataProductService.status === 'INACTIVE')" -->
|
||||
<DialogForm
|
||||
v-if="dialogServiceEdit"
|
||||
hide-footer
|
||||
no-address
|
||||
height="95vh"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, reactive, ref, watch, computed } from 'vue';
|
||||
import { onMounted, onUnmounted, reactive, ref, watch, computed } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
|
@ -275,6 +275,10 @@ const customerNameInfo = computed(() => {
|
|||
return name || '-';
|
||||
});
|
||||
|
||||
function handleWindowFocus() {
|
||||
fetchQuotationList();
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
pageState.gridView = $q.screen.lt.md ? true : false;
|
||||
navigatorStore.current.title = 'quotation.title';
|
||||
|
|
@ -312,6 +316,12 @@ onMounted(async () => {
|
|||
}
|
||||
|
||||
flowStore.rotate();
|
||||
|
||||
window.addEventListener('focus', handleWindowFocus);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('focus', handleWindowFocus);
|
||||
});
|
||||
|
||||
async function fetchQuotationList(mobileFetch?: boolean) {
|
||||
|
|
@ -767,8 +777,13 @@ async function filterBySellerId() {
|
|||
:worker-count="item.row._count.worker"
|
||||
:worker-max="item.row.workerMax || item.row._count.worker"
|
||||
:customer-name="
|
||||
item.row.customerBranch.registerName ||
|
||||
`${item.row.customerBranch.firstName || '-'} ${item.row.customerBranch.lastName || ''}`
|
||||
item.row.customerBranch.customer.customerType === 'CORP'
|
||||
? $i18n.locale === 'tha'
|
||||
? item.row.customerBranch.registerName
|
||||
: item.row.customerBranch.registerNameEN
|
||||
: $i18n.locale === 'tha'
|
||||
? `${item.row.customerBranch.firstName || '-'} ${item.row.customerBranch.lastName || ''}`
|
||||
: `${item.row.customerBranch.firstNameEN || '-'} ${item.row.customerBranch.lastNameEN || ''}`
|
||||
"
|
||||
:reporter="
|
||||
$i18n.locale === 'eng'
|
||||
|
|
|
|||
|
|
@ -1,16 +1,9 @@
|
|||
<script setup lang="ts">
|
||||
import { baseUrl, dialog } from 'stores/utils';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { QFile, QMenu } from 'quasar';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useConfigStore } from 'stores/config';
|
||||
import { formatNumberDecimal } from 'stores/utils';
|
||||
import { SaveButton, EditButton, UndoButton } from 'components/button';
|
||||
import SelectInput from 'src/components/shared/SelectInput.vue';
|
||||
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
import { reactive, ref } from 'vue';
|
||||
import { useQuotationPayment } from 'src/stores/quotations';
|
||||
import {
|
||||
PaymentPayload,
|
||||
|
|
@ -19,16 +12,25 @@ import {
|
|||
QuotationPaymentData,
|
||||
} from 'src/stores/quotations/types';
|
||||
import { dateFormatJS } from 'src/utils/datetime';
|
||||
import { QFile, QMenu } from 'quasar';
|
||||
import UploadFileCard from 'src/components/upload-file/UploadFileCard.vue';
|
||||
import { onMounted } from 'vue';
|
||||
import { DebitNote } from 'src/stores/debit-note';
|
||||
import { baseUrl, dialog, formatNumberDecimal } from 'stores/utils';
|
||||
import { useConfigStore } from 'stores/config';
|
||||
import useBranchStore from 'src/stores/branch';
|
||||
import useOptionStore from 'src/stores/options';
|
||||
|
||||
import UploadFileCard from 'src/components/upload-file/UploadFileCard.vue';
|
||||
import SelectInput from 'src/components/shared/SelectInput.vue';
|
||||
import { SaveButton, EditButton, UndoButton } from 'components/button';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { fetchListBankByBranch } = useBranchStore();
|
||||
const { mapOption } = useOptionStore();
|
||||
const configStore = useConfigStore();
|
||||
const quotationPayment = useQuotationPayment();
|
||||
const { data: config } = storeToRefs(configStore);
|
||||
|
||||
const prop = defineProps<{
|
||||
branchId: string;
|
||||
data?: Quotation | QuotationFull | DebitNote;
|
||||
readonly?: boolean;
|
||||
isDebitNote?: boolean;
|
||||
|
|
@ -39,6 +41,7 @@ const firstCodePayment = defineModel<string>('firstCodePayment');
|
|||
const refQFile = ref<InstanceType<typeof QFile>[]>([]);
|
||||
const refQMenu = ref<InstanceType<typeof QMenu>[]>([]);
|
||||
const paymentData = ref<QuotationPaymentData[]>([]);
|
||||
const accountOpt = ref<{ label: string; value: string }[]>([]);
|
||||
const formPaymentMethod = ref<
|
||||
{
|
||||
id: string;
|
||||
|
|
@ -180,7 +183,7 @@ async function selectStatus(
|
|||
payment.paymentStatus = status;
|
||||
const payload = {
|
||||
paymentStatus: payment.paymentStatus,
|
||||
date: new Date(payment.date),
|
||||
date: new Date(),
|
||||
amount: payment.amount,
|
||||
};
|
||||
const res = await quotationPayment.updateQuotationPayment(
|
||||
|
|
@ -215,6 +218,7 @@ async function triggerSubmit(id: string) {
|
|||
formPaymentMethod.value[index].isEdit = false;
|
||||
setTimeout(async () => {
|
||||
await fetchData();
|
||||
await getSlipList(paymentData.value[index], index);
|
||||
}, 300);
|
||||
}
|
||||
|
||||
|
|
@ -247,8 +251,25 @@ async function fetchData() {
|
|||
}
|
||||
}
|
||||
|
||||
async function fetchBankOption() {
|
||||
const bankOption = await fetchListBankByBranch(prop.branchId);
|
||||
accountOpt.value = bankOption
|
||||
.map((b) => {
|
||||
const name =
|
||||
`${b.accountName} ${mapOption(b.bankName)} ${mapOption(b.accountType)} ${b.accountNumber}`.trim();
|
||||
if (!name) return;
|
||||
|
||||
return {
|
||||
label: name,
|
||||
value: name,
|
||||
};
|
||||
})
|
||||
.filter((i) => !!i);
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchData();
|
||||
await fetchBankOption();
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
|
|
@ -640,95 +661,115 @@ onMounted(async () => {
|
|||
</template>
|
||||
|
||||
<!-- วิธีการรับชำระ -->
|
||||
<section
|
||||
class="q-px-md q-py-xs text-weight-medium row items-center"
|
||||
style="background-color: hsla(var(--info-bg) / 0.1)"
|
||||
>
|
||||
{{ $t('quotation.receiptDialog.paymentMethod') }}
|
||||
</section>
|
||||
<div
|
||||
class="surface-2 q-px-md q-py-sm row q-col-gutter-sm items-center"
|
||||
>
|
||||
<SelectInput
|
||||
id="input-payment-channel"
|
||||
for="input-payment-channel"
|
||||
:readonly="
|
||||
readonly ||
|
||||
(!formPaymentMethod[i].isEdit && !!payment.channel)
|
||||
"
|
||||
v-model="formPaymentMethod[i].channel"
|
||||
class="col-md-2 col-6"
|
||||
:option="[
|
||||
{
|
||||
label: $t('creditNote.label.Cash'),
|
||||
value: 'Cash',
|
||||
},
|
||||
{
|
||||
label: $t('creditNote.label.BankTransfer'),
|
||||
value: 'BankTransfer',
|
||||
},
|
||||
]"
|
||||
:label="$t('quotation.receiptDialog.paymentMethod')"
|
||||
@update:model-value="
|
||||
() => {
|
||||
if (formPaymentMethod[i].channel === 'Cash') {
|
||||
formPaymentMethod[i].reference = null;
|
||||
formPaymentMethod[i].account = null;
|
||||
}
|
||||
}
|
||||
"
|
||||
/>
|
||||
<q-input
|
||||
v-if="formPaymentMethod[i].channel === 'BankTransfer'"
|
||||
dense
|
||||
outlined
|
||||
class="col-md-3 col-6"
|
||||
v-model="formPaymentMethod[i].reference"
|
||||
:readonly="
|
||||
readonly ||
|
||||
(!formPaymentMethod[i].isEdit && !!payment.channel)
|
||||
"
|
||||
:label="$t('quotation.refNo')"
|
||||
/>
|
||||
<q-input
|
||||
v-if="formPaymentMethod[i].channel === 'BankTransfer'"
|
||||
dense
|
||||
outlined
|
||||
class="col"
|
||||
v-model="formPaymentMethod[i].account"
|
||||
:readonly="
|
||||
readonly ||
|
||||
(!formPaymentMethod[i].isEdit && !!payment.channel)
|
||||
"
|
||||
:label="$t('quotation.bankAccount')"
|
||||
/>
|
||||
<div class="q-ml-auto">
|
||||
<UndoButton
|
||||
v-if="formPaymentMethod[i].isEdit"
|
||||
icon-only
|
||||
@click="
|
||||
() => {
|
||||
formPaymentMethod[i].isEdit = false;
|
||||
formPaymentMethod[i].channel = payment.channel;
|
||||
formPaymentMethod[i].reference = payment.reference;
|
||||
formPaymentMethod[i].account = payment.account;
|
||||
}
|
||||
"
|
||||
/>
|
||||
<SaveButton
|
||||
v-if="!payment.channel || formPaymentMethod[i].isEdit"
|
||||
icon-only
|
||||
@click="triggerSubmit(payment.id)"
|
||||
/>
|
||||
<EditButton
|
||||
v-if="
|
||||
payment.channel && formPaymentMethod[i].isEdit === false
|
||||
"
|
||||
icon-only
|
||||
@click="formPaymentMethod[i].isEdit = true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="payment.paymentStatus === 'PaymentSuccess'">
|
||||
<section
|
||||
class="q-px-md q-py-xs text-weight-medium row items-center"
|
||||
style="background-color: hsla(var(--info-bg) / 0.1)"
|
||||
>
|
||||
{{ $t('quotation.receiptDialog.paymentMethod') }}
|
||||
</section>
|
||||
|
||||
<q-form
|
||||
class="column full-height"
|
||||
@submit.prevent
|
||||
@submit="triggerSubmit(payment.id)"
|
||||
>
|
||||
<div
|
||||
class="surface-2 q-px-md q-py-sm row q-col-gutter-sm items-center"
|
||||
>
|
||||
<SelectInput
|
||||
id="input-payment-channel"
|
||||
for="input-payment-channel"
|
||||
:readonly="
|
||||
readonly ||
|
||||
(!formPaymentMethod[i].isEdit && !!payment.channel)
|
||||
"
|
||||
v-model="formPaymentMethod[i].channel"
|
||||
class="col-md-2 col-6"
|
||||
:rules="[
|
||||
(val: string) => !!val || $t('form.error.required'),
|
||||
]"
|
||||
:option="[
|
||||
{
|
||||
label: $t('creditNote.label.Cash'),
|
||||
value: 'Cash',
|
||||
},
|
||||
{
|
||||
label: $t('creditNote.label.BankTransfer'),
|
||||
value: 'BankTransfer',
|
||||
},
|
||||
]"
|
||||
:label="$t('quotation.receiptDialog.paymentMethod')"
|
||||
@update:model-value="
|
||||
() => {
|
||||
if (formPaymentMethod[i].channel === 'Cash') {
|
||||
formPaymentMethod[i].reference = null;
|
||||
formPaymentMethod[i].account = null;
|
||||
}
|
||||
}
|
||||
"
|
||||
/>
|
||||
<q-input
|
||||
v-if="formPaymentMethod[i].channel === 'BankTransfer'"
|
||||
dense
|
||||
outlined
|
||||
class="col-md-3 col-6"
|
||||
v-model="formPaymentMethod[i].reference"
|
||||
:readonly="
|
||||
readonly ||
|
||||
(!formPaymentMethod[i].isEdit && !!payment.channel)
|
||||
"
|
||||
:label="$t('quotation.refNo')"
|
||||
:rules="[
|
||||
(val: string) => !!val || $t('form.error.required'),
|
||||
]"
|
||||
hide-bottom-space
|
||||
/>
|
||||
<SelectInput
|
||||
v-if="formPaymentMethod[i].channel === 'BankTransfer'"
|
||||
id="select-payment-account"
|
||||
for="select-payment-account"
|
||||
:readonly="
|
||||
readonly ||
|
||||
(!formPaymentMethod[i].isEdit && !!payment.channel)
|
||||
"
|
||||
v-model="formPaymentMethod[i].account"
|
||||
class="col"
|
||||
:option="accountOpt"
|
||||
:label="$t('quotation.bankAccount')"
|
||||
:rules="[
|
||||
(val: string) => !!val || $t('form.error.required'),
|
||||
]"
|
||||
/>
|
||||
<div class="q-ml-auto">
|
||||
<UndoButton
|
||||
v-if="formPaymentMethod[i].isEdit"
|
||||
icon-only
|
||||
@click="
|
||||
() => {
|
||||
formPaymentMethod[i].isEdit = false;
|
||||
formPaymentMethod[i].channel = payment.channel;
|
||||
formPaymentMethod[i].reference = payment.reference;
|
||||
formPaymentMethod[i].account = payment.account;
|
||||
}
|
||||
"
|
||||
/>
|
||||
<SaveButton
|
||||
v-if="!payment.channel || formPaymentMethod[i].isEdit"
|
||||
icon-only
|
||||
type="submit"
|
||||
/>
|
||||
<EditButton
|
||||
v-if="
|
||||
payment.channel && formPaymentMethod[i].isEdit === false
|
||||
"
|
||||
icon-only
|
||||
@click="formPaymentMethod[i].isEdit = true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</q-form>
|
||||
</template>
|
||||
|
||||
<!-- อัปโหลดใบเสร็จ -->
|
||||
<section
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@ type ProductGroupId = string;
|
|||
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
|
||||
|
||||
const customerBranchOption = ref<CustomerBranch>();
|
||||
|
||||
const employeeStore = useEmployeeStore();
|
||||
const route = useRoute();
|
||||
const useReceiptStore = useReceipt();
|
||||
|
|
@ -113,8 +115,6 @@ const $q = useQuasar();
|
|||
const openQuotation = ref<boolean>(false);
|
||||
const formMetadata = ref();
|
||||
|
||||
const customerBranchOption = ref<CustomerBranch>();
|
||||
|
||||
const rowsRequestList = ref<RequestData[]>([]);
|
||||
|
||||
const {
|
||||
|
|
@ -161,49 +161,7 @@ const selectedWorker = ref<
|
|||
}[];
|
||||
})[]
|
||||
>([]);
|
||||
const selectedWorkerItem = computed(() => {
|
||||
return [
|
||||
...selectedWorker.value.map((e) => ({
|
||||
foreignRefNo: e.employeePassport
|
||||
? e.employeePassport[0]?.number || '-'
|
||||
: '-',
|
||||
employeeName:
|
||||
locale.value === Lang.English
|
||||
? `${e.firstNameEN} ${e.lastNameEN}`
|
||||
: e.firstName
|
||||
? `${e.firstName} ${e.lastName}`
|
||||
: `${e.firstNameEN} ${e.lastNameEN}`,
|
||||
birthDate: dateFormatJS({ date: e.dateOfBirth }),
|
||||
gender: e.gender,
|
||||
age: calculateAge(e.dateOfBirth),
|
||||
nationality: optionStore.mapOption(e.nationality),
|
||||
documentExpireDate:
|
||||
e.employeePassport !== undefined &&
|
||||
e.employeePassport[0]?.expireDate !== undefined
|
||||
? dateFormatJS({ date: e.employeePassport[0]?.expireDate })
|
||||
: '-',
|
||||
imgUrl: e.selectedImage
|
||||
? `${API_BASE_URL}/employee/${e.id}/image/${e.selectedImage}`
|
||||
: '',
|
||||
status: e.status,
|
||||
})),
|
||||
|
||||
...newWorkerList.value.map((v: any) => ({
|
||||
foreignRefNo: v.passportNo,
|
||||
employeeName:
|
||||
locale.value === Lang.English
|
||||
? `${v.firstNameEN} ${v.lastNameEN}`
|
||||
: `${v.firstName} ${v.lastName}`,
|
||||
birthDate: dateFormatJS({ date: v.dateOfBirth }),
|
||||
gender: v.gender,
|
||||
age: calculateAge(v.dateOfBirth),
|
||||
nationality: optionStore.mapOption(v.nationality),
|
||||
documentExpireDate: '-',
|
||||
imgUrl: '',
|
||||
status: 'CREATED',
|
||||
})),
|
||||
];
|
||||
});
|
||||
const selectedWorkerItem = ref([]);
|
||||
const firstCodePayment = ref('');
|
||||
const selectedProductGroup = ref('');
|
||||
const selectedInstallmentNo = ref<number[]>([]);
|
||||
|
|
@ -238,7 +196,7 @@ function getPrice(
|
|||
) {
|
||||
if (filterHook) list = list.filter(filterHook);
|
||||
|
||||
return list.reduce(
|
||||
const value = list.reduce(
|
||||
(a, c) => {
|
||||
if (
|
||||
selectedInstallmentNo.value.length > 0 &&
|
||||
|
|
@ -278,6 +236,8 @@ function getPrice(
|
|||
finalPrice: 0,
|
||||
},
|
||||
);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
const summaryPrice = computed(() => getPrice(productServiceList.value));
|
||||
|
|
@ -556,7 +516,7 @@ async function convertDataToFormSubmit() {
|
|||
),
|
||||
);
|
||||
|
||||
selectedWorker.value.forEach((v, i) => {
|
||||
selectedWorkerItem.value.forEach((v, i) => {
|
||||
if (v.attachment !== undefined) {
|
||||
v.attachment.forEach((value) => {
|
||||
fileItemNewWorker.value.push({
|
||||
|
|
@ -573,7 +533,7 @@ async function convertDataToFormSubmit() {
|
|||
|
||||
quotationFormData.value.worker = JSON.parse(
|
||||
JSON.stringify([
|
||||
...selectedWorker.value.map((v) => {
|
||||
...selectedWorkerItem.value.map((v) => {
|
||||
{
|
||||
return v.id;
|
||||
}
|
||||
|
|
@ -806,7 +766,40 @@ function toggleDeleteProduct(index: number) {
|
|||
}
|
||||
|
||||
async function assignWorkerToSelectedWorker() {
|
||||
selectedWorker.value = quotationFormData.value.worker;
|
||||
selectedWorkerItem.value = quotationFormData.value.worker.map((e) => {
|
||||
return {
|
||||
id: e.id,
|
||||
foreignRefNo: e.employeePassport
|
||||
? e.employeePassport[0]?.number || '-'
|
||||
: '-',
|
||||
employeeName:
|
||||
locale.value === Lang.English
|
||||
? `${e.firstNameEN} ${e.lastNameEN}`
|
||||
: `${e.firstName || e.firstNameEN} ${e.lastName || e.lastNameEN}`,
|
||||
birthDate: dateFormatJS({ date: e.dateOfBirth }),
|
||||
gender: e.gender,
|
||||
age: calculateAge(e.dateOfBirth),
|
||||
nationality: optionStore.mapOption(e.nationality),
|
||||
documentExpireDate:
|
||||
e.employeePassport !== undefined &&
|
||||
e.employeePassport[0]?.expireDate !== undefined
|
||||
? dateFormatJS({ date: e.employeePassport[0]?.expireDate })
|
||||
: '-',
|
||||
imgUrl: e.selectedImage
|
||||
? `${API_BASE_URL}/employee/${e.id}/image/${e.selectedImage}`
|
||||
: '',
|
||||
employeePassport: e.employeePassport,
|
||||
status: e.status,
|
||||
workerNew: false,
|
||||
lastNameEN: e.lastNameEN,
|
||||
lastName: e.lastName,
|
||||
middleNameEN: e.middleNameEN,
|
||||
middleName: e.middleName,
|
||||
firstNameEN: e.firstNameEN,
|
||||
firstName: e.firstName,
|
||||
namePrefix: e.namePrefix,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function convertToTable(nodes: Node[]) {
|
||||
|
|
@ -865,21 +858,21 @@ function convertToTable(nodes: Node[]) {
|
|||
|
||||
function convertEmployeeToTable(selected: Employee[]) {
|
||||
productServiceList.value.forEach((v) => {
|
||||
if (selectedWorker.value.length === 0 && v.amount === 1) v.amount -= 1;
|
||||
if (selectedWorkerItem.value.length === 0 && v.amount === 1) v.amount -= 1;
|
||||
|
||||
v.amount = Math.max(
|
||||
v.amount + selected.length - selectedWorker.value.length,
|
||||
v.amount + selected.length - selectedWorkerItem.value.length,
|
||||
1,
|
||||
);
|
||||
|
||||
const oldWorkerId: string[] = [];
|
||||
const newWorkerIndex: number[] = [];
|
||||
|
||||
selectedWorker.value.forEach((item, i) => {
|
||||
selectedWorkerItem.value.forEach((item, i) => {
|
||||
if (v.workerIndex.includes(i)) oldWorkerId.push(item.id);
|
||||
});
|
||||
selected.forEach((item, i) => {
|
||||
if (selectedWorker.value.find((n) => item.id === n.id)) return;
|
||||
if (selectedWorkerItem.value.find((n) => item.id === n.id)) return;
|
||||
newWorkerIndex.push(i);
|
||||
});
|
||||
|
||||
|
|
@ -892,7 +885,7 @@ function convertEmployeeToTable(selected: Employee[]) {
|
|||
pageState.employeeModal = false;
|
||||
quotationFormData.value.workerMax = Math.max(
|
||||
quotationFormData.value.workerMax || 1,
|
||||
selectedWorker.value.length,
|
||||
selectedWorkerItem.value.length,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -965,6 +958,71 @@ function viewProductFile(data: ProductRelation) {
|
|||
pageState.imageDialogUrl = base64 ? base64[1] : '';
|
||||
}
|
||||
|
||||
function combineWorker(newWorker: any, oldWorker: any) {
|
||||
selectedWorkerItem.value = [
|
||||
...oldWorker.map((e) => ({
|
||||
id: e.id,
|
||||
foreignRefNo: e.employeePassport
|
||||
? e.employeePassport[0]?.number || '-'
|
||||
: '-',
|
||||
employeeName:
|
||||
locale.value === Lang.English
|
||||
? `${e.firstNameEN} ${e.lastNameEN}`
|
||||
: `${e.firstName || e.firstNameEN} ${e.lastName || e.lastNameEN}`,
|
||||
birthDate: dateFormatJS({ date: e.dateOfBirth }),
|
||||
gender: e.gender,
|
||||
age: calculateAge(e.dateOfBirth),
|
||||
nationality: optionStore.mapOption(e.nationality),
|
||||
documentExpireDate:
|
||||
e.employeePassport !== undefined &&
|
||||
e.employeePassport[0]?.expireDate !== undefined
|
||||
? dateFormatJS({ date: e.employeePassport[0]?.expireDate })
|
||||
: '-',
|
||||
imgUrl: e.selectedImage
|
||||
? `${API_BASE_URL}/employee/${e.id}/image/${e.selectedImage}`
|
||||
: '',
|
||||
|
||||
employeePassport: e.employeePassport,
|
||||
status: e.status,
|
||||
workerNew: false,
|
||||
lastNameEN: e.lastNameEN,
|
||||
lastName: e.lastName,
|
||||
middleNameEN: e.middleNameEN,
|
||||
middleName: e.middleName,
|
||||
firstNameEN: e.firstNameEN,
|
||||
firstName: e.firstName,
|
||||
namePrefix: e.namePrefix,
|
||||
})),
|
||||
|
||||
...newWorker.map((v: any) => ({
|
||||
id: v.id,
|
||||
foreignRefNo: v.passportNo || '-',
|
||||
employeeName:
|
||||
locale.value === Lang.English
|
||||
? `${v.firstNameEN} ${v.lastNameEN}`
|
||||
: `${v.firstName || v.firstNameEN} ${v.lastName || v.lastNameEN}`,
|
||||
birthDate: dateFormatJS({ date: v.dateOfBirth }),
|
||||
gender: v.gender,
|
||||
age: calculateAge(v.dateOfBirth),
|
||||
nationality: optionStore.mapOption(v.nationality),
|
||||
documentExpireDate: '-',
|
||||
imgUrl: '',
|
||||
status: 'CREATED',
|
||||
|
||||
lastNameEN: v.lastNameEN,
|
||||
lastName: v.lastName,
|
||||
middleNameEN: v.middleNameEN,
|
||||
middleName: v.middleName,
|
||||
firstNameEN: v.firstNameEN,
|
||||
firstName: v.firstName,
|
||||
namePrefix: v.namePrefix,
|
||||
|
||||
dateOfBirth: v.dateOfBirth,
|
||||
workerNew: true,
|
||||
})),
|
||||
];
|
||||
}
|
||||
|
||||
const sessionData = ref<Record<string, any>>();
|
||||
|
||||
onMounted(async () => {
|
||||
|
|
@ -1036,7 +1094,7 @@ watch(
|
|||
() => quotationFormData.value.customerBranchId,
|
||||
async (v) => {
|
||||
if (!v) return;
|
||||
selectedWorker.value = [];
|
||||
selectedWorkerItem.value = [];
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -1052,6 +1110,15 @@ watch(
|
|||
|
||||
const productServiceNodes = ref<ProductTree>([]);
|
||||
|
||||
watch(customerBranchOption, () => {
|
||||
if (!customerBranchOption.value) return;
|
||||
|
||||
quotationFormData.value.contactName =
|
||||
customerBranchOption.value.contactName || '';
|
||||
quotationFormData.value.contactTel =
|
||||
customerBranchOption.value.contactTel || '';
|
||||
});
|
||||
|
||||
watch(
|
||||
() => productServiceList.value,
|
||||
() => {
|
||||
|
|
@ -1087,7 +1154,19 @@ watch(customerBranchOption, () => {
|
|||
// }
|
||||
|
||||
function storeDataLocal() {
|
||||
quotationFormData.value.productServiceList = productService.value;
|
||||
const tempProductService = productService.value.map((v) => {
|
||||
return {
|
||||
...v,
|
||||
vat: v.product[agentPrice ? 'agentPriceCalcVat' : 'calcVat']
|
||||
? precisionRound(
|
||||
((v.pricePerUnit * (1 + (config?.value.vat || 0.07)) * v.amount -
|
||||
v.discount) /
|
||||
(1 + (config?.value.vat || 0.07))) *
|
||||
0.07,
|
||||
)
|
||||
: 0,
|
||||
};
|
||||
});
|
||||
|
||||
localStorage.setItem(
|
||||
'quotation-preview',
|
||||
|
|
@ -1096,7 +1175,7 @@ function storeDataLocal() {
|
|||
codeInvoice: code.value,
|
||||
codePayment: firstCodePayment.value,
|
||||
...quotationFormData.value,
|
||||
productServiceList: productService.value,
|
||||
productServiceList: tempProductService,
|
||||
},
|
||||
meta: {
|
||||
source: {
|
||||
|
|
@ -1116,7 +1195,7 @@ function storeDataLocal() {
|
|||
workName: quotationFormData.value.workName,
|
||||
dueDate: quotationFormData.value.dueDate,
|
||||
},
|
||||
selectedWorker: selectedWorker.value,
|
||||
selectedWorker: selectedWorkerItem.value,
|
||||
createdBy: quotationFormState.value.createdBy('tha'),
|
||||
agentPrice: agentPrice.value,
|
||||
},
|
||||
|
|
@ -1201,10 +1280,10 @@ async function getWorkerFromCriteria(
|
|||
if (!ret) return false; // error, do not close dialog
|
||||
|
||||
const deduplicate = ret.result.filter(
|
||||
(a) => !selectedWorker.value.find((b) => a.id === b.id),
|
||||
(a) => !selectedWorkerItem.value.find((b) => a.id === b.id),
|
||||
);
|
||||
|
||||
convertEmployeeToTable([...deduplicate, ...selectedWorker.value]);
|
||||
convertEmployeeToTable([...deduplicate, ...selectedWorkerItem.value]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1594,15 +1673,15 @@ function covertToNode() {
|
|||
(v) =>
|
||||
(quotationFormData.workerMax = Math.max(
|
||||
v,
|
||||
selectedWorker.length,
|
||||
selectedWorkerItem.length,
|
||||
))
|
||||
"
|
||||
:employee-amount="
|
||||
quotationFormData.workerMax || selectedWorker.length
|
||||
quotationFormData.workerMax || selectedWorkerItem.length
|
||||
"
|
||||
:readonly="readonly"
|
||||
:rows="selectedWorkerItem"
|
||||
@delete="(i) => deleteItem(selectedWorker, i)"
|
||||
@delete="(i) => deleteItem(selectedWorkerItem, i)"
|
||||
/>
|
||||
</div>
|
||||
</q-expansion-item>
|
||||
|
|
@ -1846,7 +1925,7 @@ function covertToNode() {
|
|||
installments: quotationFormData.paySplit,
|
||||
},
|
||||
'quotation-labor': {
|
||||
name: selectedWorker.map(
|
||||
name: selectedWorkerItem.map(
|
||||
(v, i) =>
|
||||
`${i + 1}. ` +
|
||||
`${v.employeePassport.length !== 0 ? v.employeePassport[0].number + '_' : ''}${v.namePrefix}.${v.firstNameEN ? `${v.firstNameEN} ${v.lastNameEN}` : `${v.firstName} ${v.lastName}`} `.toUpperCase(),
|
||||
|
|
@ -1939,6 +2018,7 @@ function covertToNode() {
|
|||
view !== View.Receipt &&
|
||||
view !== View.Complete
|
||||
"
|
||||
:branch-id="quotationFull.registeredBranchId"
|
||||
:readonly="
|
||||
isRoleInclude(['sale', 'head_of_sale']) ||
|
||||
!canAccess('quotation', 'edit')
|
||||
|
|
@ -2389,13 +2469,12 @@ function covertToNode() {
|
|||
<!-- add employee quotation -->
|
||||
|
||||
<QuotationFormWorkerSelect
|
||||
:preselect-worker="selectedWorker"
|
||||
:preselect-worker="selectedWorkerItem"
|
||||
:customerBranchId="quotationFormData.customerBranchId"
|
||||
v-model:open="pageState.employeeModal"
|
||||
v-model:new-worker-list="newWorkerList"
|
||||
@success="
|
||||
(v) => {
|
||||
selectedWorker = v.worker;
|
||||
combineWorker(v.newWorker, v.worker);
|
||||
}
|
||||
"
|
||||
/>
|
||||
|
|
@ -2438,7 +2517,7 @@ function covertToNode() {
|
|||
<!-- add Worker -->
|
||||
<QuotationFormWorkerAddDialog
|
||||
v-if="quotationFormState.source"
|
||||
:disabled-worker-id="selectedWorker.map((v) => v.id)"
|
||||
:disabled-worker-id="selectedWorkerItem.map((v) => v.id)"
|
||||
:product-service-list="quotationFormState.source.productServiceList"
|
||||
:quotation-id="quotationFormState.source.id"
|
||||
:customer-branch-id="quotationFormState.source.customerBranchId"
|
||||
|
|
|
|||
|
|
@ -234,6 +234,7 @@ watch(
|
|||
<section class="row q-col-gutter-sm col-12 items-center">
|
||||
<SelectInput
|
||||
class="col-md-6 col-12"
|
||||
id="select-pay-type"
|
||||
:label="$t('quotation.payType')"
|
||||
:option="
|
||||
taskOrder
|
||||
|
|
@ -241,7 +242,6 @@ watch(
|
|||
: payTypeOption
|
||||
"
|
||||
:readonly="readonly || debitNote"
|
||||
id="pay-type"
|
||||
:model-value="payType"
|
||||
@update:model-value="
|
||||
(v) => {
|
||||
|
|
@ -275,6 +275,7 @@ watch(
|
|||
</div>
|
||||
<q-input
|
||||
v-model="paySplitCount"
|
||||
id="input-pay-split-count"
|
||||
:readonly="readonly || payType === 'Split'"
|
||||
class="col-3"
|
||||
type="number"
|
||||
|
|
@ -311,6 +312,7 @@ watch(
|
|||
<q-input
|
||||
:readonly="readonly"
|
||||
:label="$t('general.name')"
|
||||
:id="`input-period-name-${i}`"
|
||||
v-if="payType === 'SplitCustom'"
|
||||
v-model="period.name"
|
||||
class="col q-mx-sm"
|
||||
|
|
@ -320,6 +322,7 @@ watch(
|
|||
<q-input
|
||||
:readonly="readonly || payType === 'Split'"
|
||||
class="col q-mx-sm"
|
||||
:id="`input-period-amount-${i}`"
|
||||
:label="$t('quotation.amount')"
|
||||
:model-value="
|
||||
amount4Show[i] || commaInput(period.amount.toString())
|
||||
|
|
@ -377,6 +380,7 @@ watch(
|
|||
|
||||
<DatePicker
|
||||
v-if="payType === 'BillFull'"
|
||||
id="datepicker-bill-date"
|
||||
:readonly
|
||||
class="col-12"
|
||||
:label="$t('quotation.callDueDate')"
|
||||
|
|
@ -484,7 +488,11 @@ watch(
|
|||
<div class="q-pa-sm row surface-2 items-center text-weight-bold">
|
||||
{{ $t('quotation.totalPriceBaht') }}
|
||||
|
||||
<span class="q-ml-auto" style="color: var(--brand-1)">
|
||||
<span
|
||||
class="q-ml-auto"
|
||||
style="color: var(--brand-1)"
|
||||
id="value-final-price"
|
||||
>
|
||||
{{
|
||||
payType === 'SplitCustom' && view === View.Invoice
|
||||
? formatNumberDecimal(Math.max(installmentAmount || 0, 0), 2) || 0
|
||||
|
|
|
|||
|
|
@ -341,12 +341,13 @@ watch(() => state.search, getWorkerList);
|
|||
>
|
||||
<div
|
||||
style="display: inline-block; margin-inline: auto"
|
||||
v-if="workerList.length === 0"
|
||||
v-if="workerList.length === 0 && state.search"
|
||||
>
|
||||
<NoData :not-found="!!state.search" />
|
||||
</div>
|
||||
|
||||
<TableWorker
|
||||
v-else
|
||||
v-model:selected="workerSelected"
|
||||
:rows="workerList"
|
||||
:disabledWorkerId
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ const props = withDefaults(
|
|||
defineProps<{
|
||||
customerBranchId?: string;
|
||||
disabledWorkerId?: string[];
|
||||
preselectWorker?: Employee[];
|
||||
preselectWorker?: (Employee & { workerNew: boolean })[];
|
||||
}>(),
|
||||
{},
|
||||
);
|
||||
|
|
@ -133,7 +133,7 @@ const optionStore = useOptionStore();
|
|||
const employeeStore = useEmployeeStore();
|
||||
|
||||
const open = defineModel<boolean>('open', { default: false });
|
||||
const newWorkerList = defineModel<
|
||||
const newWorkerList = ref<
|
||||
(EmployeeWorker & {
|
||||
attachment?: {
|
||||
name?: string;
|
||||
|
|
@ -143,7 +143,7 @@ const newWorkerList = defineModel<
|
|||
_meta?: Record<string, any>;
|
||||
}[];
|
||||
})[]
|
||||
>('newWorkerList', { default: [] });
|
||||
>([]);
|
||||
const workerSelected = ref<Employee[]>([]);
|
||||
const workerList = ref<Employee[]>([]);
|
||||
const importWorkerCriteria = ref<{
|
||||
|
|
@ -208,7 +208,13 @@ function getEmployeeImageUrl(item: Employee) {
|
|||
|
||||
function init() {
|
||||
if (props.preselectWorker) {
|
||||
workerSelected.value = JSON.parse(JSON.stringify(props.preselectWorker));
|
||||
workerSelected.value = JSON.parse(
|
||||
JSON.stringify(props.preselectWorker.filter((v) => !v.workerNew)),
|
||||
);
|
||||
|
||||
newWorkerList.value = JSON.parse(
|
||||
JSON.stringify(props.preselectWorker.filter((v) => v.workerNew)),
|
||||
);
|
||||
}
|
||||
getWorkerList();
|
||||
}
|
||||
|
|
@ -608,11 +614,14 @@ watch(
|
|||
solid
|
||||
id="btn-success"
|
||||
@click="
|
||||
(emits('success', {
|
||||
worker: workerSelected,
|
||||
newWorker: newWorkerList,
|
||||
}),
|
||||
(open = false))
|
||||
() => {
|
||||
$emit('success', {
|
||||
worker: workerSelected,
|
||||
newWorker: newWorkerList,
|
||||
});
|
||||
|
||||
open = false;
|
||||
}
|
||||
"
|
||||
>
|
||||
{{ $t('general.select', { msg: $t('quotation.employeeList') }) }}
|
||||
|
|
@ -634,9 +643,11 @@ watch(
|
|||
if (employeeFormState.currentTab === 'personalInfo') {
|
||||
const currentEmployeeId =
|
||||
await employeeFormStore.submitPersonal(onCreateImageList);
|
||||
quotationForm.injectNewEmployee({
|
||||
data: { ...currentFromDataEmployee, id: currentEmployeeId },
|
||||
});
|
||||
newWorkerList.push(
|
||||
quotationForm.injectNewEmployee({
|
||||
data: { ...currentFromDataEmployee, id: currentEmployeeId },
|
||||
}),
|
||||
);
|
||||
employeeFormState.isEmployeeEdit = false;
|
||||
employeeFormState.dialogType = 'info';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ export const useQuotationForm = defineStore('form-quotation', () => {
|
|||
file?: File;
|
||||
_meta?: Record<string, any>;
|
||||
}[];
|
||||
workerNew: boolean;
|
||||
})[]
|
||||
>([]);
|
||||
|
||||
|
|
@ -220,7 +221,7 @@ export const useQuotationForm = defineStore('form-quotation', () => {
|
|||
},
|
||||
callback?: () => void,
|
||||
) {
|
||||
newWorkerList.value.push({
|
||||
const temp = {
|
||||
//passportNo: obj.data.passportNo,
|
||||
//documentExpireDate: obj.data.documentExpireDate,
|
||||
id: obj.data.id,
|
||||
|
|
@ -235,9 +236,12 @@ export const useQuotationForm = defineStore('form-quotation', () => {
|
|||
gender: obj.data.gender,
|
||||
dateOfBirth: obj.data.dateOfBirth,
|
||||
attachment: obj.data.attachment,
|
||||
});
|
||||
workerNew: true,
|
||||
};
|
||||
|
||||
callback?.();
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
function dialogDelete(callback: () => void) {
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ const props = withDefaults(
|
|||
readonly?: boolean;
|
||||
listDocument: string[];
|
||||
currentId: { customer: string; employee: string };
|
||||
prefix?: string;
|
||||
}>(),
|
||||
{
|
||||
listDocument: () => [],
|
||||
|
|
@ -244,14 +245,14 @@ function changeCustomerTab(opts: { tab: 'customer' | 'employee' }) {
|
|||
<nav class="q-ml-auto row" v-if="!readonly">
|
||||
<CancelButton
|
||||
v-if="state.isEdit"
|
||||
id="btn-info-basic-undo"
|
||||
:id="`btn-docs-${props.prefix || 'nome'}-info-basic-undo`"
|
||||
icon-only
|
||||
type="button"
|
||||
@click.stop="triggerCancel"
|
||||
/>
|
||||
<EditButton
|
||||
v-if="!state.isEdit"
|
||||
id="btn-info-basic-edit"
|
||||
:id="`btn-docs-${props.prefix || 'nome'}-info-basic-edit`"
|
||||
icon-only
|
||||
@click.stop="triggerEdit"
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { useRequestList } from 'src/stores/request-list';
|
|||
const props = defineProps<{
|
||||
readonly?: boolean;
|
||||
step: Step;
|
||||
prefix?: string;
|
||||
}>();
|
||||
|
||||
const requestListStore = useRequestList();
|
||||
|
|
@ -100,21 +101,21 @@ function assignToForm() {
|
|||
<nav class="q-ml-auto row" v-if="!readonly">
|
||||
<UndoButton
|
||||
v-if="state.isEdit"
|
||||
id="btn-info-basic-undo"
|
||||
:id="`btn-duty-${props.prefix || 'nome'}-info-basic-undo`"
|
||||
icon-only
|
||||
type="button"
|
||||
@click.stop="triggerUndo"
|
||||
/>
|
||||
<SaveButton
|
||||
v-if="state.isEdit"
|
||||
id="btn-info-basic-save"
|
||||
:id="`btn-duty-${props.prefix || 'nome'}-info-basic-save`"
|
||||
icon-only
|
||||
type="submit"
|
||||
@click.stop="triggerSubmit"
|
||||
/>
|
||||
<EditButton
|
||||
v-if="!state.isEdit"
|
||||
id="btn-info-basic-edit"
|
||||
:id="`btn-duty-${props.prefix || 'nome'}-info-basic-edit`"
|
||||
icon-only
|
||||
@click.stop="triggerEdit"
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ const props = defineProps<{
|
|||
readonly?: boolean;
|
||||
step: Step;
|
||||
requestWorkId: string;
|
||||
prefix?: string;
|
||||
}>();
|
||||
|
||||
const requestListStore = useRequestList();
|
||||
|
|
@ -90,21 +91,21 @@ function assignToForm() {
|
|||
<nav class="q-ml-auto row" v-if="!readonly">
|
||||
<UndoButton
|
||||
v-if="state.isEdit"
|
||||
id="btn-info-basic-undo"
|
||||
:id="`btn-form-${props.prefix || 'nome'}-info-basic-undo`"
|
||||
icon-only
|
||||
type="button"
|
||||
@click.stop="triggerUndo"
|
||||
/>
|
||||
<SaveButton
|
||||
v-if="state.isEdit"
|
||||
id="btn-info-basic-save"
|
||||
:id="`btn-form-${props.prefix || 'nome'}-info-basic-save`"
|
||||
icon-only
|
||||
type="submit"
|
||||
@click.stop="triggerSubmit"
|
||||
/>
|
||||
<EditButton
|
||||
v-if="!state.isEdit"
|
||||
id="btn-info-basic-edit"
|
||||
:id="`btn-form-${props.prefix || 'nome'}-info-basic-edit`"
|
||||
icon-only
|
||||
@click.stop="triggerEdit"
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ const responsibleUserId = defineModel<string>('responsibleUserId', {
|
|||
defineProps<{
|
||||
readonly?: boolean;
|
||||
districtId?: string;
|
||||
prefix?: string;
|
||||
}>();
|
||||
|
||||
watch(responsibleUserLocal, (lhs, rhs) => {
|
||||
|
|
@ -27,6 +28,8 @@ watch(responsibleUserLocal, (lhs, rhs) => {
|
|||
:label="$t('requestList.localEmployee')"
|
||||
:disable="readonly"
|
||||
class="col"
|
||||
:id="`${prefix || 'nome'}-radio-local-employee`"
|
||||
:for="`${prefix || 'nome'}-radio-local-employee`"
|
||||
/>
|
||||
<q-radio
|
||||
v-model="responsibleUserLocal"
|
||||
|
|
@ -34,6 +37,8 @@ watch(responsibleUserLocal, (lhs, rhs) => {
|
|||
:label="$t('requestList.nonLocalEmployee')"
|
||||
:disable="readonly"
|
||||
class="col"
|
||||
:id="`${prefix || 'nome'}-radio-non-local-employee`"
|
||||
:for="`${prefix || 'nome'}-radio-non-local-employee`"
|
||||
/>
|
||||
<div class="col" />
|
||||
<div class="offset-md-7"></div>
|
||||
|
|
@ -52,6 +57,8 @@ watch(responsibleUserLocal, (lhs, rhs) => {
|
|||
}"
|
||||
:readonly
|
||||
:label="$t('general.select', { msg: $t('personnel.MESSENGER') })"
|
||||
:id="`${prefix || 'nome'}-select-user`"
|
||||
:for="`${prefix || 'nome'}-select-user`"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ function triggerCancel(id: string) {
|
|||
const res = await requestListStore.cancelRequest(id);
|
||||
if (res) {
|
||||
fetchList();
|
||||
fetchStats();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ const props = defineProps<{
|
|||
step: Step;
|
||||
responsibleAreaDistrictId?: string;
|
||||
defaultMessenger?: string;
|
||||
prefix?: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
|
@ -116,21 +117,21 @@ function assignToForm() {
|
|||
<nav class="q-ml-auto row" v-if="!readonly">
|
||||
<UndoButton
|
||||
v-if="state.isEdit"
|
||||
id="btn-info-basic-undo"
|
||||
:id="`btn-messenger-${props.prefix || 'nome'}-info-basic-undo`"
|
||||
icon-only
|
||||
type="button"
|
||||
@click.stop="triggerUndo"
|
||||
/>
|
||||
<SaveButton
|
||||
v-if="state.isEdit"
|
||||
id="btn-info-basic-save"
|
||||
:id="`btn-messenger-${props.prefix || 'nome'}-info-basic-save`"
|
||||
icon-only
|
||||
type="submit"
|
||||
@click.stop="(e) => refForm?.submit(e)"
|
||||
/>
|
||||
<EditButton
|
||||
v-if="!state.isEdit"
|
||||
id="btn-info-basic-edit"
|
||||
:id="`btn-messenger-${props.prefix || 'nome'}-info-basic-edit`"
|
||||
icon-only
|
||||
@click.stop="triggerEdit"
|
||||
type="button"
|
||||
|
|
@ -157,6 +158,7 @@ function assignToForm() {
|
|||
v-model:responsible-user-local="formData.responsibleUserLocal"
|
||||
v-model:responsible-user-id="formData.responsibleUserId"
|
||||
:district-id="responsibleAreaDistrictId"
|
||||
:prefix
|
||||
/>
|
||||
</q-form>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ function changeableStatus(currentStatus?: RequestWorkStatus) {
|
|||
</script>
|
||||
<template>
|
||||
<q-expansion-item
|
||||
:id="`expansion-${product?.name || name}`"
|
||||
:for="`expansion-${product?.name || name}`"
|
||||
dense
|
||||
:class="{ 'status-unpaid': !paySuccess }"
|
||||
class="overflow-hidden"
|
||||
|
|
@ -146,6 +148,8 @@ function changeableStatus(currentStatus?: RequestWorkStatus) {
|
|||
<div class="q-ml-auto q-gutter-y-xs">
|
||||
<div class="justify-end flex">
|
||||
<q-btn-dropdown
|
||||
:id="`btn-dropdown-${product?.name || name}`"
|
||||
:for="`btn-dropdown-${product?.name || name}`"
|
||||
:disable="
|
||||
readonly || changeableStatus(status?.workStatus).length === 0
|
||||
"
|
||||
|
|
@ -197,6 +201,8 @@ function changeableStatus(currentStatus?: RequestWorkStatus) {
|
|||
<q-list dense>
|
||||
<q-item
|
||||
v-for="(value, index) in changeableStatus(status?.workStatus)"
|
||||
:id="`btn-dropdown-${product?.name || name}-${value}`"
|
||||
:for="`btn-dropdown-${product?.name || name}-${value}`"
|
||||
:key="index"
|
||||
clickable
|
||||
v-close-popup
|
||||
|
|
@ -271,15 +277,15 @@ function changeableStatus(currentStatus?: RequestWorkStatus) {
|
|||
}
|
||||
|
||||
:deep(
|
||||
i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-item__toggle-icon--rotated
|
||||
) {
|
||||
i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-item__toggle-icon--rotated
|
||||
) {
|
||||
color: var(--brand-1);
|
||||
}
|
||||
|
||||
:deep(
|
||||
.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
|
||||
) {
|
||||
.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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ const props = withDefaults(
|
|||
readonly?: boolean;
|
||||
propertiesToShow: (PropString | PropNumber | PropDate | PropOptions)[];
|
||||
requestListData: RequestData;
|
||||
prefix?: string;
|
||||
}>(),
|
||||
{
|
||||
id: '',
|
||||
|
|
@ -128,7 +129,7 @@ defineEmits<{
|
|||
<nav class="q-ml-auto row" v-if="!readonly">
|
||||
<UndoButton
|
||||
v-if="state.isEdit"
|
||||
id="btn-info-basic-undo"
|
||||
:id="`btn-properties-${props.prefix || 'nome'}-info-basic-undo`"
|
||||
icon-only
|
||||
type="button"
|
||||
@click.stop="triggerUndo"
|
||||
|
|
@ -136,13 +137,14 @@ defineEmits<{
|
|||
<SaveButton
|
||||
v-if="state.isEdit"
|
||||
id="btn-info-basic-save"
|
||||
:id="`btn-properties-${props.prefix || 'nome'}-info-basic-save`"
|
||||
icon-only
|
||||
type="submit"
|
||||
@click.stop="triggerSubmit"
|
||||
/>
|
||||
<EditButton
|
||||
v-if="!state.isEdit"
|
||||
id="btn-info-basic-edit"
|
||||
:id="`btn-properties-${props.prefix || 'nome'}-info-basic-edit`"
|
||||
icon-only
|
||||
@click.stop="triggerEdit"
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -881,6 +881,7 @@ function toEmployee(employee: RequestData['employee']) {
|
|||
:readonly="value._readonly"
|
||||
ref="refDocumentExpansion"
|
||||
:attributes="value.attributes"
|
||||
:prefix="value.productService.product.name"
|
||||
@change-status="
|
||||
(opt) => {
|
||||
triggerChangeStatusFile({
|
||||
|
|
@ -928,6 +929,7 @@ function toEmployee(employee: RequestData['employee']) {
|
|||
<MessengerExpansion
|
||||
v-if="value._messengerExpansion"
|
||||
:readonly="value._readonly"
|
||||
:prefix="value.productService.product.name"
|
||||
:default-messenger="
|
||||
value.stepStatus[pageState.currentStep - 1]
|
||||
? undefined
|
||||
|
|
@ -953,6 +955,7 @@ function toEmployee(employee: RequestData['employee']) {
|
|||
<DutyExpansion
|
||||
v-if="value._dutyExpansion"
|
||||
:readonly="value._readonly"
|
||||
:prefix="value.productService.product.name"
|
||||
:step="{
|
||||
step: pageState.currentStep,
|
||||
requestWorkId: value.id || '',
|
||||
|
|
@ -966,6 +969,7 @@ function toEmployee(employee: RequestData['employee']) {
|
|||
v-if="value._formExpansion"
|
||||
:request-work-id="value.id"
|
||||
:readonly="value._readonly"
|
||||
:prefix="value.productService.product.name"
|
||||
:step="{
|
||||
step: pageState.currentStep,
|
||||
requestWorkId: value.id || '',
|
||||
|
|
@ -979,6 +983,7 @@ function toEmployee(employee: RequestData['employee']) {
|
|||
:request-list-data="data"
|
||||
:id="value.id"
|
||||
:readonly="value._readonly"
|
||||
:prefix="value.productService.product.name"
|
||||
:properties-to-show="
|
||||
value.productService.work?.attributes.workflowStep[
|
||||
pageState.currentStep - 1
|
||||
|
|
|
|||
|
|
@ -190,6 +190,8 @@ function handleCheckAll() {
|
|||
>
|
||||
<q-th v-if="checkable">
|
||||
<q-checkbox
|
||||
:for="`list-checkbox-all`"
|
||||
:id="`list-checkbox-all`"
|
||||
v-if="selected.length > 0"
|
||||
:model-value="
|
||||
selected.length ===
|
||||
|
|
@ -229,6 +231,8 @@ function handleCheckAll() {
|
|||
>
|
||||
<q-td v-if="checkable">
|
||||
<q-checkbox
|
||||
:for="`list-checkbox-${props.rowIndex + 1}`"
|
||||
:id="`list-checkbox-${props.rowIndex + 1}`"
|
||||
:disable="
|
||||
selected.length > 0 &&
|
||||
!listSameArea.includes(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts" setup>
|
||||
// NOTE: Library
|
||||
import { computed, onMounted, reactive, watch, ref } from 'vue';
|
||||
import { computed, onMounted, onUnmounted, reactive, watch, ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
|
@ -86,6 +86,7 @@ async function fetchTaskOrderList(opts?: { page?: number; pageSize?: number }) {
|
|||
});
|
||||
}
|
||||
if (res) {
|
||||
taskOrderStore.getTaskOrderStats();
|
||||
data.value = res.result;
|
||||
pageState.total = res.total;
|
||||
pageMax.value = Math.ceil(res.total / pageSize.value);
|
||||
|
|
@ -146,6 +147,10 @@ async function deleteTaskOrder(id: string) {
|
|||
});
|
||||
}
|
||||
|
||||
function handleWindowFocus() {
|
||||
fetchTaskOrderList();
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
pageState.gridView = $q.screen.lt.md ? true : false;
|
||||
navigatorStore.current.title = 'taskOrder.title';
|
||||
|
|
@ -156,6 +161,12 @@ onMounted(async () => {
|
|||
if (route.query['tab'] && typeof route.query['tab'] === 'string') {
|
||||
pageState.currentTab = route.query['tab'];
|
||||
}
|
||||
|
||||
window.addEventListener('focus', handleWindowFocus);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('focus', handleWindowFocus);
|
||||
});
|
||||
|
||||
watch(
|
||||
|
|
@ -218,7 +229,11 @@ watch(
|
|||
color: hsl(var(--info-bg));
|
||||
"
|
||||
>
|
||||
{{ Object.values(stats).reduce((s, v) => s + v, 0) }}
|
||||
{{
|
||||
pageState.isMessenger
|
||||
? pageState.total
|
||||
: stats[pageState.currentTab as TaskOrderStatus]
|
||||
}}
|
||||
</q-badge>
|
||||
<q-btn
|
||||
class="q-ml-sm"
|
||||
|
|
|
|||
|
|
@ -295,6 +295,7 @@ function assignTempGroup() {
|
|||
class="bordered-b"
|
||||
>
|
||||
<q-expansion-item
|
||||
:id="`expansion-product-${product.code}`"
|
||||
dense
|
||||
class="overflow-hidden"
|
||||
switch-toggle-side
|
||||
|
|
@ -348,6 +349,7 @@ function assignTempGroup() {
|
|||
</FormGroupHead>
|
||||
<div class="q-pa-md full-width">
|
||||
<TableEmployee
|
||||
:id="`table-employee-${product.code}`"
|
||||
checkbox-on
|
||||
check-all
|
||||
select-ready
|
||||
|
|
@ -371,9 +373,15 @@ function assignTempGroup() {
|
|||
</section>
|
||||
|
||||
<template #footer>
|
||||
<CancelButton class="q-ml-auto" outlined @click="close" />
|
||||
<CancelButton
|
||||
id="btn-dialog-cancel"
|
||||
class="q-ml-auto"
|
||||
outlined
|
||||
@click="close"
|
||||
/>
|
||||
<SaveButton
|
||||
:label="$t('general.select')"
|
||||
id="btn-dialog-select"
|
||||
class="q-ml-sm"
|
||||
icon="mdi-check"
|
||||
solid
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ function disableCheckAll() {
|
|||
<template>
|
||||
<q-table
|
||||
flat
|
||||
id="table-employee"
|
||||
bordered
|
||||
row-key="id"
|
||||
v-bind="props"
|
||||
|
|
@ -273,6 +274,7 @@ function disableCheckAll() {
|
|||
>
|
||||
<q-th v-if="checkboxOn" class="relative-position">
|
||||
<q-checkbox
|
||||
id="checkbox-check-all"
|
||||
v-if="checkAll"
|
||||
:disable="disableCheckAll()"
|
||||
:model-value="
|
||||
|
|
@ -305,6 +307,7 @@ function disableCheckAll() {
|
|||
class="absolute-right row items-center"
|
||||
>
|
||||
<q-btn
|
||||
id="btn-change-all-status"
|
||||
flat
|
||||
dense
|
||||
rounded
|
||||
|
|
@ -339,6 +342,7 @@ function disableCheckAll() {
|
|||
{{ $t(`taskOrder.status.Complete`) }}
|
||||
</q-item>
|
||||
<q-item
|
||||
id="menu-item-redo"
|
||||
clickable
|
||||
v-close-popup
|
||||
class="items-center"
|
||||
|
|
@ -358,6 +362,7 @@ function disableCheckAll() {
|
|||
{{ $t(`taskOrder.status.Redo`) }}
|
||||
</q-item>
|
||||
<q-item
|
||||
id="menu-item-restart"
|
||||
clickable
|
||||
v-close-popup
|
||||
class="items-center"
|
||||
|
|
@ -379,6 +384,7 @@ function disableCheckAll() {
|
|||
</q-list>
|
||||
<q-list v-if="!validate" dense>
|
||||
<q-item
|
||||
id="menu-item-success"
|
||||
clickable
|
||||
v-close-popup
|
||||
class="items-center"
|
||||
|
|
@ -398,6 +404,7 @@ function disableCheckAll() {
|
|||
{{ $t(`taskOrder.status.Success`) }}
|
||||
</q-item>
|
||||
<q-item
|
||||
id="menu-item-failed"
|
||||
clickable
|
||||
v-close-popup
|
||||
class="items-center"
|
||||
|
|
@ -480,6 +487,7 @@ function disableCheckAll() {
|
|||
<q-td>
|
||||
<span
|
||||
class="cursor-pointer link"
|
||||
:id="`link-request-list-${props.row.request.code}`"
|
||||
@click="goToRequestList(props.row.request.id)"
|
||||
>
|
||||
{{ props.row.request.code }}
|
||||
|
|
@ -489,6 +497,7 @@ function disableCheckAll() {
|
|||
<q-td>
|
||||
<span
|
||||
class="cursor-pointer link"
|
||||
:id="`link-quotation-${props.row.request.quotation?.code}`"
|
||||
@click="goToQuotation(props.row.request.quotation)"
|
||||
>
|
||||
{{ props.row.request.quotation?.code }}
|
||||
|
|
@ -496,7 +505,11 @@ function disableCheckAll() {
|
|||
</q-td>
|
||||
|
||||
<q-td v-if="stepOn" class="text-left">
|
||||
<div v-if="props.row._template" class="column text-left">
|
||||
<div
|
||||
v-if="props.row._template"
|
||||
class="column text-left"
|
||||
:id="`template-step-${props.row.request.code}`"
|
||||
>
|
||||
<span>{{ props.row._template.templateName }}</span>
|
||||
<span class="app-text-muted text-caption">
|
||||
{{ $t('flow.stepNo', { msg: props.row._template.step }) }}
|
||||
|
|
@ -522,7 +535,10 @@ function disableCheckAll() {
|
|||
</template>
|
||||
</q-img>
|
||||
</q-avatar>
|
||||
<div class="column text-left q-ml-sm">
|
||||
<div
|
||||
class="column text-left q-ml-sm"
|
||||
:id="`employee-name-${props.row.request.employee?.code}`"
|
||||
>
|
||||
<div>
|
||||
{{ getEmployeeName(props.row, { locale: $i18n.locale }) }}
|
||||
</div>
|
||||
|
|
@ -532,6 +548,7 @@ function disableCheckAll() {
|
|||
</div>
|
||||
</div>
|
||||
<Icon
|
||||
:id="`icon-gender-${props.row.request.employee?.code}`"
|
||||
class="q-ml-md"
|
||||
:class="`app-text-${props.row.request.employee?.gender}`"
|
||||
:icon="`material-symbols:${props.row.request.employee?.gender}`"
|
||||
|
|
@ -539,13 +556,18 @@ function disableCheckAll() {
|
|||
/>
|
||||
</div>
|
||||
</q-td>
|
||||
<q-td>{{ calculateAge(props.row.request.employee?.dateOfBirth) }}</q-td>
|
||||
<q-td>
|
||||
<q-td :id="`employee-age-${props.row.request.employee?.code}`">
|
||||
{{ calculateAge(props.row.request.employee?.dateOfBirth) }}
|
||||
</q-td>
|
||||
<q-td :id="`employee-nationality-${props.row.request.employee?.code}`">
|
||||
{{
|
||||
useOptionStore().mapOption(props.row.request.employee?.nationality)
|
||||
}}
|
||||
</q-td>
|
||||
<q-td>
|
||||
<q-td
|
||||
:id="`quotation-due-date-${props.row.request.quotation?.code}`"
|
||||
v-if="!statusOn"
|
||||
>
|
||||
{{
|
||||
dateFormatJS({
|
||||
date: props.row.request.quotation?.dueDate,
|
||||
|
|
@ -555,13 +577,16 @@ function disableCheckAll() {
|
|||
})
|
||||
}}
|
||||
</q-td>
|
||||
<q-td>
|
||||
<q-td
|
||||
:id="`expiration-date-${props.row.request.quotation?.code}`"
|
||||
v-if="!statusOn"
|
||||
>
|
||||
<ExpirationDate
|
||||
:expiration-date="new Date(props.row.request.quotation?.dueDate)"
|
||||
/>
|
||||
</q-td>
|
||||
|
||||
<q-td>
|
||||
<q-td v-if="!statusOn">
|
||||
<BadgeComponent
|
||||
v-if="props.row.request.quotation?.urgent"
|
||||
icon="mdi-fire"
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ const emit = defineEmits<{
|
|||
</script>
|
||||
<template>
|
||||
<q-table
|
||||
id="table-task-order"
|
||||
v-bind="props"
|
||||
:columns="column"
|
||||
bordered
|
||||
|
|
@ -133,7 +134,11 @@ const emit = defineEmits<{
|
|||
:props="props"
|
||||
>
|
||||
<q-th v-if="selection !== 'none'">
|
||||
<q-checkbox v-model="props.selected" size="sm" />
|
||||
<q-checkbox
|
||||
id="checkbox-select-all-task"
|
||||
v-model="props.selected"
|
||||
size="sm"
|
||||
/>
|
||||
</q-th>
|
||||
<q-th v-for="col in props.cols" :key="col.name" :props="props">
|
||||
{{ col.label && $t(col.label) }}
|
||||
|
|
@ -149,13 +154,21 @@ const emit = defineEmits<{
|
|||
>
|
||||
<q-tr class="text-center" :class="{ urgent: props.row.urgent }">
|
||||
<q-td v-if="selection !== 'none'">
|
||||
<q-checkbox v-model="props.selected" size="sm" />
|
||||
<q-checkbox
|
||||
:id="`checkbox-task-${props.row.code}`"
|
||||
v-model="props.selected"
|
||||
size="sm"
|
||||
/>
|
||||
</q-td>
|
||||
<q-td v-if="visibleColumns.includes('order')">
|
||||
{{ props.rowIndex + 1 }}
|
||||
</q-td>
|
||||
<q-td v-if="visibleColumns.includes('taskName')" class="text-left">
|
||||
<div>
|
||||
<q-td
|
||||
v-if="visibleColumns.includes('taskName')"
|
||||
class="text-left"
|
||||
:id="`task-name-${props.row.code}`"
|
||||
>
|
||||
<div :id="`task-name-div-${props.row.code}`">
|
||||
{{ props.row.taskName || '-' }}
|
||||
<q-tooltip :delay="300">
|
||||
{{ props.row.taskName || '-' }}
|
||||
|
|
@ -170,31 +183,51 @@ const emit = defineEmits<{
|
|||
}}
|
||||
</div>
|
||||
</q-td>
|
||||
<q-td v-if="visibleColumns.includes('issueBranch')">
|
||||
<q-td
|
||||
v-if="visibleColumns.includes('issueBranch')"
|
||||
:id="`task-issue-branch-${props.row.code}`"
|
||||
>
|
||||
{{
|
||||
$i18n.locale === 'eng'
|
||||
? props.row.registeredBranch.nameEN || '-'
|
||||
: props.row.registeredBranch.name || '-'
|
||||
}}
|
||||
</q-td>
|
||||
<q-td v-if="visibleColumns.includes('institution')">
|
||||
<q-td
|
||||
v-if="visibleColumns.includes('institution')"
|
||||
:id="`task-institution-${props.row.code}`"
|
||||
>
|
||||
{{
|
||||
$i18n.locale === 'eng'
|
||||
? props.row.institution.nameEN || '-'
|
||||
: props.row.institution.name || '-'
|
||||
}}
|
||||
</q-td>
|
||||
<q-td v-if="visibleColumns.includes('createdAt')">
|
||||
<q-td
|
||||
v-if="visibleColumns.includes('createdAt')"
|
||||
:id="`task-created-at-${props.row.code}`"
|
||||
>
|
||||
{{ dateFormatJS({ date: props.row.createdAt }) || '-' }}
|
||||
{{ dateFormatJS({ date: props.row.createdAt, timeOnly: true }) }}
|
||||
</q-td>
|
||||
<q-td v-if="visibleColumns.includes('createdBy')" class="text-left">
|
||||
<q-td
|
||||
v-if="visibleColumns.includes('createdBy')"
|
||||
class="text-left"
|
||||
:id="`task-created-by-${props.row.code}`"
|
||||
>
|
||||
{{ getCreatedByName(props.row, $i18n) }}
|
||||
</q-td>
|
||||
<q-td v-if="visibleColumns.includes('contactTel')">
|
||||
<q-td
|
||||
v-if="visibleColumns.includes('contactTel')"
|
||||
:id="`task-contact-tel-${props.row.code}`"
|
||||
>
|
||||
{{ props.row.contactTel || '-' }}
|
||||
</q-td>
|
||||
<q-td v-if="visibleColumns.includes('contactName')" class="text-left">
|
||||
<q-td
|
||||
v-if="visibleColumns.includes('contactName')"
|
||||
class="text-left"
|
||||
:id="`task-contact-name-${props.row.code}`"
|
||||
>
|
||||
{{ props.row.contactName || '-' }}
|
||||
</q-td>
|
||||
<q-td v-if="visibleColumns.includes('taskStatus')">
|
||||
|
|
@ -202,11 +235,12 @@ const emit = defineEmits<{
|
|||
hide-icon
|
||||
:hsla-color="taskOrderStatus(props.row.taskOrderStatus, 'color')"
|
||||
:title="$t(taskOrderStatus(props.row.taskOrderStatus, 'status'))"
|
||||
:id="`badge-task-status-${props.row.code}`"
|
||||
/>
|
||||
</q-td>
|
||||
<q-td v-if="selection === 'none'">
|
||||
<q-btn
|
||||
:id="`btn-eye-${props.row.taskName}`"
|
||||
:id="`btn-view-task-${props.row.code}`"
|
||||
icon="mdi-eye-outline"
|
||||
size="sm"
|
||||
dense
|
||||
|
|
@ -221,7 +255,7 @@ const emit = defineEmits<{
|
|||
canAccess('taskOrder', 'edit')
|
||||
"
|
||||
:hide-delete="!canAccess('taskOrder', 'create')"
|
||||
:idName="`btn-kebab-${props.row.taskName}`"
|
||||
:idName="`btn-kebab-${props.row.code}`"
|
||||
status="'ACTIVE'"
|
||||
hide-toggle
|
||||
@view="$emit('view', props.row)"
|
||||
|
|
@ -233,6 +267,7 @@ const emit = defineEmits<{
|
|||
<q-btn
|
||||
dense
|
||||
flat
|
||||
:id="`btn-sub-row-${props.row.code}`"
|
||||
class="rounded"
|
||||
@click.stop="
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -60,16 +60,25 @@ function inactiveCheck() {
|
|||
</script>
|
||||
<template>
|
||||
<div
|
||||
:id="`readonly-status-${status}`"
|
||||
:for="`readonly-status-${status}`"
|
||||
v-if="readonly"
|
||||
class="row rounded bordered surface-2 items-center justify-center q-pa-xs no-wrap"
|
||||
:style="`color: hsl(var(--${currStatus?.color}-bg))`"
|
||||
>
|
||||
<q-icon :name="currStatus?.icon" class="q-pr-xs" size="xs" />
|
||||
{{ $t(`taskOrder.status.${status}`) }}
|
||||
<q-icon
|
||||
:id="`readonly-status-icon-${status}`"
|
||||
:name="currStatus?.icon"
|
||||
class="q-pr-xs"
|
||||
size="xs"
|
||||
/>
|
||||
<span :id="`readonly-status-label-${status}`">{{ $t(`taskOrder.status.${status}`) }}</span>
|
||||
</div>
|
||||
|
||||
<div v-else class="row items-center justify-center no-wrap">
|
||||
<q-btn-dropdown
|
||||
:id="`btn-dropdown-status-${status}`"
|
||||
:for="`btn-dropdown-status-${status}`"
|
||||
dense
|
||||
unelevated
|
||||
:label="
|
||||
|
|
@ -110,6 +119,8 @@ function inactiveCheck() {
|
|||
>
|
||||
<q-list v-if="!noAction" dense>
|
||||
<q-item
|
||||
:for="`menu-item-status-${v.value}`"
|
||||
:id="`menu-item-status-${v.value}`"
|
||||
v-for="(v, index) in type === 'order'
|
||||
? {
|
||||
Success: taskStatusOrderToggle.filter(
|
||||
|
|
@ -147,6 +158,8 @@ function inactiveCheck() {
|
|||
</q-btn-dropdown>
|
||||
|
||||
<q-btn
|
||||
:id="`btn-failed-remark-${status}`"
|
||||
:for="`btn-failed-remark-${status}`"
|
||||
v-if="currStatus?.value === TaskStatus.Failed"
|
||||
flat
|
||||
dense
|
||||
|
|
@ -186,9 +199,9 @@ function inactiveCheck() {
|
|||
}
|
||||
|
||||
:deep(
|
||||
.hide-icon
|
||||
i.q-icon.mdi.mdi-chevron-down.q-btn-dropdown__arrow.q-btn-dropdown__arrow-container
|
||||
) {
|
||||
.hide-icon
|
||||
i.q-icon.mdi.mdi-chevron-down.q-btn-dropdown__arrow.q-btn-dropdown__arrow-container
|
||||
) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ const fileData = defineModel<
|
|||
</script>
|
||||
<template>
|
||||
<q-expansion-item
|
||||
id="expansion-additional-file"
|
||||
dense
|
||||
class="overflow-hidden bordered full-width"
|
||||
switch-toggle-side
|
||||
|
|
@ -41,6 +42,7 @@ const fileData = defineModel<
|
|||
|
||||
<main class="q-px-md q-py-sm surface-1">
|
||||
<UploadFileSection
|
||||
id="upload-additional-file"
|
||||
multiple
|
||||
:layout="$q.screen.gt.sm ? 'column' : 'row'"
|
||||
:readonly
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ const contactTel = defineModel<string>('contactTel');
|
|||
</script>
|
||||
<template>
|
||||
<q-expansion-item
|
||||
id="expansion-document"
|
||||
default-opened
|
||||
dense
|
||||
class="overflow-hidden bordered full-width"
|
||||
|
|
@ -38,13 +39,16 @@ const contactTel = defineModel<string>('contactTel');
|
|||
|
||||
<main class="q-px-md q-py-sm surface-1 row q-col-gutter-sm">
|
||||
<SelectBranch
|
||||
id="select-issue-branch"
|
||||
:readonly
|
||||
required
|
||||
class="col-md-4 col-12"
|
||||
:label="`${$t('taskOrder.issueBranch')}${$i18n.locale === 'tha' ? $t('taskOrder.title') : ''}`"
|
||||
v-model:value="registeredBranchId"
|
||||
auto-select-on-single
|
||||
/>
|
||||
<SelectInstitution
|
||||
id="select-agencies"
|
||||
:readonly
|
||||
required
|
||||
class="col-md-4 col-12"
|
||||
|
|
@ -55,6 +59,7 @@ const contactTel = defineModel<string>('contactTel');
|
|||
auto-select-on-single
|
||||
/>
|
||||
<DatePicker
|
||||
id="datepicker-issue-date"
|
||||
:label="$t('taskOrder.issueDate')"
|
||||
class="col-md-2 col-6"
|
||||
:model-value="issueDate || new Date(Date.now())"
|
||||
|
|
@ -62,6 +67,7 @@ const contactTel = defineModel<string>('contactTel');
|
|||
:disabled="!readonly"
|
||||
/>
|
||||
<q-input
|
||||
id="input-task-code"
|
||||
:label="$t('taskOrder.code')"
|
||||
outlined
|
||||
dense
|
||||
|
|
@ -72,6 +78,7 @@ const contactTel = defineModel<string>('contactTel');
|
|||
/>
|
||||
|
||||
<q-input
|
||||
id="input-task-name"
|
||||
:readonly
|
||||
:label="$t('general.name', { msg: $t('taskOrder.title') })"
|
||||
outlined
|
||||
|
|
@ -80,6 +87,7 @@ const contactTel = defineModel<string>('contactTel');
|
|||
v-model="taskName"
|
||||
/>
|
||||
<q-input
|
||||
id="input-contact-name"
|
||||
:readonly
|
||||
:label="$t('taskOrder.contactName')"
|
||||
outlined
|
||||
|
|
@ -88,6 +96,7 @@ const contactTel = defineModel<string>('contactTel');
|
|||
v-model="contactName"
|
||||
/>
|
||||
<q-input
|
||||
id="input-contact-tel"
|
||||
:readonly
|
||||
:label="$t('general.telephone')"
|
||||
outlined
|
||||
|
|
@ -96,6 +105,7 @@ const contactTel = defineModel<string>('contactTel');
|
|||
v-model="contactTel"
|
||||
/>
|
||||
<q-input
|
||||
id="input-made-by"
|
||||
:readonly
|
||||
:label="$t('taskOrder.madeBy')"
|
||||
outlined
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ const summaryPrice = defineModel<{
|
|||
</script>
|
||||
<template>
|
||||
<q-expansion-item
|
||||
id="expansion-payment"
|
||||
dense
|
||||
class="overflow-hidden bordered full-width"
|
||||
switch-toggle-side
|
||||
|
|
@ -47,6 +48,7 @@ const summaryPrice = defineModel<{
|
|||
|
||||
<main class="q-px-md q-py-sm surface-1">
|
||||
<QuotationFormInfo
|
||||
id="form-info-payment"
|
||||
task-order
|
||||
:task-order-complete="complete"
|
||||
v-model:pay-type="payType"
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ function taskOrderStatus(value: TaskStatus) {
|
|||
</script>
|
||||
<template>
|
||||
<q-expansion-item
|
||||
id="expansion-product-list"
|
||||
dense
|
||||
class="overflow-hidden bordered full-width"
|
||||
switch-toggle-side
|
||||
|
|
@ -123,9 +124,11 @@ function taskOrderStatus(value: TaskStatus) {
|
|||
<span
|
||||
class="row items-center justify-between full-width"
|
||||
style="min-height: 31.01px"
|
||||
id="header-product-list"
|
||||
>
|
||||
{{ $t('general.information', { msg: $t('taskOrder.productList') }) }}
|
||||
<AddButton
|
||||
id="btn-add-product"
|
||||
icon-only
|
||||
@click.stop="$emit('addProduct')"
|
||||
v-if="!readonly"
|
||||
|
|
@ -135,6 +138,7 @@ function taskOrderStatus(value: TaskStatus) {
|
|||
|
||||
<main class="q-px-md q-py-sm surface-1">
|
||||
<q-table
|
||||
id="table-product-list"
|
||||
:columns="
|
||||
creditNote
|
||||
? productColumn.filter(
|
||||
|
|
@ -180,7 +184,7 @@ function taskOrderStatus(value: TaskStatus) {
|
|||
} & Omit<Parameters<QTableSlots['body']>[0], 'row'>"
|
||||
>
|
||||
<q-tr class="text-center">
|
||||
<q-td>
|
||||
<q-td :id="`product-order-${props.rowIndex}`">
|
||||
{{ props.rowIndex + 1 }}
|
||||
</q-td>
|
||||
<q-td>
|
||||
|
|
@ -206,16 +210,17 @@ function taskOrderStatus(value: TaskStatus) {
|
|||
</q-td>
|
||||
<q-td class="text-left" v-if="!creditNote">
|
||||
<BadgeComponent
|
||||
:id="`badge-status-${props.row.product.code}`"
|
||||
hide-icon
|
||||
:hsla-color="taskOrderStatus(props.row.product.taskStatus)"
|
||||
:title="`${$t(`taskOrder.status.${props.row.product.taskStatus}`)} ${!!props.row.product.totalNotStatusComplete ? $t('general.totalPeople', { meg: props.row.product.totalNotStatusComplete }) : ''}`"
|
||||
/>
|
||||
</q-td>
|
||||
|
||||
<q-td>
|
||||
<q-td :id="`product-amount-${props.row.product.code}`">
|
||||
{{ props.row.list.length }}
|
||||
</q-td>
|
||||
<q-td class="text-right">
|
||||
<q-td :id="`product-price-per-unit-${props.row.product.code}`" class="text-right">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
calcPricePerUnit(props.row.product) +
|
||||
|
|
@ -230,6 +235,7 @@ function taskOrderStatus(value: TaskStatus) {
|
|||
<!-- TODO: display price detail -->
|
||||
<q-td align="center" v-if="!creditNote">
|
||||
<q-input
|
||||
:id="`input-discount-${props.row.product.code}`"
|
||||
:readonly
|
||||
:bg-color="readonly ? 'transparent' : ''"
|
||||
dense
|
||||
|
|
@ -267,7 +273,11 @@ function taskOrderStatus(value: TaskStatus) {
|
|||
/>
|
||||
</q-td>
|
||||
<!-- before vat -->
|
||||
<q-td class="text-right" v-if="!creditNote">
|
||||
<q-td
|
||||
:id="`product-price-before-vat-${props.row.product.code}`"
|
||||
class="text-right"
|
||||
v-if="!creditNote"
|
||||
>
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
props.row.product.serviceChargeCalcVat
|
||||
|
|
@ -284,7 +294,11 @@ function taskOrderStatus(value: TaskStatus) {
|
|||
}}
|
||||
</q-td>
|
||||
<!-- vat -->
|
||||
<q-td class="text-right" v-if="!creditNote">
|
||||
<q-td
|
||||
:id="`product-vat-${props.row.product.code}`"
|
||||
class="text-right"
|
||||
v-if="!creditNote"
|
||||
>
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
props.row.product.serviceChargeCalcVat
|
||||
|
|
@ -301,7 +315,7 @@ function taskOrderStatus(value: TaskStatus) {
|
|||
}}
|
||||
</q-td>
|
||||
<!-- total -->
|
||||
<q-td class="text-right">
|
||||
<q-td :id="`product-total-price-${props.row.product.code}`" class="text-right">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
calcPrice(props.row.product, props.row.list.length),
|
||||
|
|
@ -311,6 +325,7 @@ function taskOrderStatus(value: TaskStatus) {
|
|||
</q-td>
|
||||
<q-td>
|
||||
<q-btn
|
||||
:id="`btn-toggle-employee-${props.row.product.code}`"
|
||||
dense
|
||||
flat
|
||||
class="rounded"
|
||||
|
|
@ -334,6 +349,7 @@ function taskOrderStatus(value: TaskStatus) {
|
|||
<q-tr v-show="currentBtnOpen[props.rowIndex]" :props="props">
|
||||
<q-td colspan="100%" style="padding: 16px">
|
||||
<TableEmployee
|
||||
:id="`table-employee-in-product-${props.row.product.code}`"
|
||||
:step-on="!creditNote"
|
||||
:status-on="creditNote"
|
||||
:rows="props.row.list"
|
||||
|
|
@ -351,7 +367,9 @@ function taskOrderStatus(value: TaskStatus) {
|
|||
})
|
||||
}}
|
||||
</span>
|
||||
<div class="surface-3 q-px-sm rounded">{{ taskList.length }}</div>
|
||||
<div class="surface-3 q-px-sm rounded" id="total-product-count">
|
||||
{{ taskList.length }}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</q-expansion-item>
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ const getToolbarConfig = computed(() => {
|
|||
</script>
|
||||
<template>
|
||||
<q-expansion-item
|
||||
id="expansion-remark"
|
||||
dense
|
||||
class="overflow-hidden bordered full-width"
|
||||
switch-toggle-side
|
||||
|
|
@ -48,6 +49,7 @@ const getToolbarConfig = computed(() => {
|
|||
|
||||
<main class="surface-1 q-pa-md full-width">
|
||||
<q-editor
|
||||
id="editor-remark"
|
||||
dense
|
||||
:readonly="readonly || !remarkWrite"
|
||||
:model-value="
|
||||
|
|
@ -90,6 +92,7 @@ const getToolbarConfig = computed(() => {
|
|||
<template v-if="!readonly" v-slot:toggle>
|
||||
<div class="text-caption row no-wrap q-px-sm">
|
||||
<MainButton
|
||||
id="btn-remark-view"
|
||||
:solid="!remarkWrite"
|
||||
icon="mdi-eye-outline"
|
||||
color="0 0% 40%"
|
||||
|
|
@ -102,6 +105,7 @@ const getToolbarConfig = computed(() => {
|
|||
{{ $t('general.view', { msg: $t('general.example') }) }}
|
||||
</MainButton>
|
||||
<MainButton
|
||||
id="btn-remark-edit"
|
||||
:solid="remarkWrite"
|
||||
icon="mdi-pencil-outline"
|
||||
color="0 0% 40%"
|
||||
|
|
|
|||
|
|
@ -47,11 +47,17 @@ defineProps<{
|
|||
<div class="row q-col-gutter-sm q-px-md q-py-sm">
|
||||
<DataDisplay
|
||||
class="col-md col-6"
|
||||
id="dd-recipient"
|
||||
:label="$t('taskOrder.recipientOrSender')"
|
||||
>
|
||||
<template #value>
|
||||
<q-avatar size="md" class="q-mr-xs">
|
||||
<q-img class="text-center" :ratio="1" :src="contactUrl">
|
||||
<q-img
|
||||
:id="`img-avatar-${contactName}`"
|
||||
class="text-center"
|
||||
:ratio="1"
|
||||
:src="contactUrl"
|
||||
>
|
||||
<template #error>
|
||||
<div
|
||||
class="no-padding full-width full-height flex items-center justify-center"
|
||||
|
|
@ -59,6 +65,7 @@ defineProps<{
|
|||
>
|
||||
<q-img
|
||||
v-if="gender"
|
||||
:id="`img-gender-${contactName}`"
|
||||
:src="
|
||||
gender === 'male'
|
||||
? '/no-img-man.png'
|
||||
|
|
@ -67,6 +74,7 @@ defineProps<{
|
|||
/>
|
||||
<q-icon
|
||||
v-else
|
||||
:id="`icon-avatar-${contactName}`"
|
||||
size="sm"
|
||||
name="mdi-account-outline"
|
||||
style="color: white"
|
||||
|
|
@ -81,18 +89,21 @@ defineProps<{
|
|||
|
||||
<DataDisplay
|
||||
class="col-md col-6"
|
||||
id="dd-telephone"
|
||||
:label="$t('general.telephone')"
|
||||
:value="contactTel || '-'"
|
||||
/>
|
||||
|
||||
<DataDisplay
|
||||
class="col-md col-6"
|
||||
id="dd-email"
|
||||
:label="$t('form.email')"
|
||||
:value="email || '-'"
|
||||
/>
|
||||
|
||||
<DataDisplay
|
||||
class="col-md col-6"
|
||||
id="dd-work-start-date"
|
||||
:label="$t('taskOrder.workStartDate')"
|
||||
>
|
||||
<template #value>
|
||||
|
|
@ -106,6 +117,7 @@ defineProps<{
|
|||
|
||||
<DataDisplay
|
||||
class="col-md col-6"
|
||||
id="dd-work-submission-date"
|
||||
:label="$t('taskOrder.workSubmissionDate')"
|
||||
>
|
||||
<template #value>
|
||||
|
|
@ -117,9 +129,14 @@ defineProps<{
|
|||
</template>
|
||||
</DataDisplay>
|
||||
|
||||
<DataDisplay class="col-md col-6" :label="$t('general.status')">
|
||||
<DataDisplay
|
||||
id="dd-status"
|
||||
class="col-md col-6"
|
||||
:label="$t('general.status')"
|
||||
>
|
||||
<template #value>
|
||||
<BadgeComponent
|
||||
:id="`badge-status-${contactName}`"
|
||||
v-if="status"
|
||||
hide-icon
|
||||
:title="
|
||||
|
|
|
|||
|
|
@ -814,7 +814,7 @@ watch(
|
|||
>
|
||||
<section class="banner" :class="{ dark: $q.dark.isActive }"></section>
|
||||
<div style="flex: 1" class="row items-center">
|
||||
<RouterLink to="/task-order">
|
||||
<RouterLink to="/task-order" id="link-task-order">
|
||||
<q-img src="/icons/favicon-512x512.png" width="3rem" />
|
||||
</RouterLink>
|
||||
<span class="column text-h6 text-bold q-ml-md">
|
||||
|
|
@ -863,6 +863,7 @@ watch(
|
|||
<!-- TODO: replace step and status -->
|
||||
<StateButton
|
||||
v-for="i in statusTabForm"
|
||||
:id="`btn-status-${i.title}`"
|
||||
:key="i.title"
|
||||
:label="$t(`taskOrder.${i.title}`)"
|
||||
:status-active="i.active?.()"
|
||||
|
|
@ -916,6 +917,7 @@ watch(
|
|||
"
|
||||
>
|
||||
<DocumentExpansion
|
||||
id="expansion-document"
|
||||
:readonly="!['create', 'edit'].includes(state.mode || '')"
|
||||
v-model:registered-branch-id="currentFormData.registeredBranchId"
|
||||
v-model:institution-id="currentFormData.institutionId"
|
||||
|
|
@ -935,6 +937,7 @@ watch(
|
|||
/>
|
||||
</q-form>
|
||||
<ProductExpansion
|
||||
id="expansion-product"
|
||||
ref="refProductExpansion"
|
||||
v-if="
|
||||
view === TaskOrderStatus.Pending ||
|
||||
|
|
@ -947,6 +950,7 @@ watch(
|
|||
/>
|
||||
|
||||
<PaymentExpansion
|
||||
id="expansion-payment"
|
||||
v-model:summary-price="summaryPrice"
|
||||
:complete="view === TaskOrderStatus.Complete"
|
||||
v-if="
|
||||
|
|
@ -956,6 +960,7 @@ watch(
|
|||
/>
|
||||
|
||||
<AdditionalFileExpansion
|
||||
id="expansion-additional-file"
|
||||
:readonly="!['create', 'edit'].includes(state.mode || '')"
|
||||
v-if="
|
||||
view === TaskOrderStatus.Pending ||
|
||||
|
|
@ -1014,6 +1019,7 @@ watch(
|
|||
/>
|
||||
<!-- TODO: blind remark, urgent -->
|
||||
<RemarkExpansion
|
||||
id="expansion-remark"
|
||||
v-if="
|
||||
view === TaskOrderStatus.Pending ||
|
||||
view === TaskOrderStatus.Complete
|
||||
|
|
@ -1038,6 +1044,7 @@ watch(
|
|||
"
|
||||
>
|
||||
<InfoMessengerExpansion
|
||||
:id="`expansion-messenger-${messengerIndex}`"
|
||||
v-for="(v, messengerIndex) in messengerListGroup"
|
||||
:key="messengerIndex"
|
||||
:gender="getMessengerName(v.responsibleUser, { gender: true })"
|
||||
|
|
@ -1073,6 +1080,7 @@ watch(
|
|||
class="bordered-b"
|
||||
>
|
||||
<q-expansion-item
|
||||
:id="`expansion-product-${productIndex}`"
|
||||
dense
|
||||
class="overflow-hidden"
|
||||
switch-toggle-side
|
||||
|
|
@ -1246,6 +1254,7 @@ watch(
|
|||
<nav class="row justify-end">
|
||||
<MainButton
|
||||
class="q-mr-auto"
|
||||
id="btn-view-example"
|
||||
v-if="currentFormData.id"
|
||||
outlined
|
||||
icon="mdi-play-box-outline"
|
||||
|
|
@ -1260,10 +1269,16 @@ watch(
|
|||
{{ $t('general.view', { msg: $t('general.example') }) }}
|
||||
</MainButton>
|
||||
<div class="row" style="gap: var(--size-2)">
|
||||
<UndoButton outlined @click="undo()" v-if="state.mode === 'edit'" />
|
||||
<UndoButton
|
||||
id="btn-undo"
|
||||
outlined
|
||||
@click="undo()"
|
||||
v-if="state.mode === 'edit'"
|
||||
/>
|
||||
<CancelButton
|
||||
v-if="state.mode !== 'edit'"
|
||||
:label="$t('dialog.action.close')"
|
||||
id="btn-close"
|
||||
outlined
|
||||
@click="closeTab()"
|
||||
/>
|
||||
|
|
@ -1275,12 +1290,14 @@ watch(
|
|||
"
|
||||
>
|
||||
<SaveButton
|
||||
id="btn-save"
|
||||
v-if="state.mode && ['create', 'edit'].includes(state.mode)"
|
||||
@click="() => formDocument.submit()"
|
||||
solid
|
||||
/>
|
||||
<EditButton
|
||||
v-else
|
||||
id="btn-edit"
|
||||
class="no-print"
|
||||
@click="state.mode = 'edit'"
|
||||
solid
|
||||
|
|
@ -1288,6 +1305,7 @@ watch(
|
|||
</template>
|
||||
<SaveButton
|
||||
v-if="
|
||||
canAccess('taskOrder', 'edit') &&
|
||||
state.mode !== 'create' &&
|
||||
view === TaskOrderStatus.Validate &&
|
||||
fullTaskOrder?.taskOrderStatus !== TaskOrderStatus.Pending &&
|
||||
|
|
@ -1326,6 +1344,7 @@ watch(
|
|||
"
|
||||
:label="$t('taskOrder.confirmEndWork')"
|
||||
icon="mdi-check"
|
||||
id="btn-confirm-end-work"
|
||||
solid
|
||||
></SaveButton>
|
||||
</div>
|
||||
|
|
@ -1335,6 +1354,7 @@ watch(
|
|||
|
||||
<!-- SEC: Dialog -->
|
||||
<SelectReadyRequestWork
|
||||
id="dialog-select-request-work"
|
||||
:task-list-group="taskListGroup"
|
||||
:fetch-params="{ readyToTask: true }"
|
||||
v-model:open="pageState.productDialog"
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ function tooltip(id: string) {
|
|||
<main class="q-py-sm q-px-md scroll">
|
||||
<q-select
|
||||
:readonly
|
||||
id="select-fail-task"
|
||||
dense
|
||||
outlined
|
||||
multiple
|
||||
|
|
@ -131,6 +132,7 @@ function tooltip(id: string) {
|
|||
>
|
||||
<template #selected-item="scope">
|
||||
<q-chip
|
||||
:id="`chip-fail-task-${taskStatusList[scope.index].code}`"
|
||||
dense
|
||||
:removable="!readonly"
|
||||
@remove="scope.removeAtIndex(scope.index)"
|
||||
|
|
@ -170,6 +172,7 @@ function tooltip(id: string) {
|
|||
|
||||
<template #option="scope">
|
||||
<q-item
|
||||
:id="`option-fail-task-${scope.opt.request.code}`"
|
||||
clickable
|
||||
v-bind="scope.itemProps"
|
||||
class="row items-start col-12 no-padding"
|
||||
|
|
@ -230,6 +233,7 @@ function tooltip(id: string) {
|
|||
|
||||
<SelectInput
|
||||
:readonly
|
||||
id="select-fail-type"
|
||||
:option="[
|
||||
{
|
||||
label: $t('taskOrder.documentSubmitFailed'),
|
||||
|
|
@ -260,6 +264,7 @@ function tooltip(id: string) {
|
|||
|
||||
<div v-if="failedType === 'other'" class="q-mt-sm rounded">
|
||||
<q-editor
|
||||
id="editor-fail-comment"
|
||||
dense
|
||||
flat
|
||||
v-model="failedComment"
|
||||
|
|
@ -279,8 +284,18 @@ function tooltip(id: string) {
|
|||
</main>
|
||||
|
||||
<template #footer v-if="!readonly">
|
||||
<CancelButton class="q-ml-auto" outlined @click="$emit('close')" />
|
||||
<SaveButton class="q-ml-sm" solid @click="submit" />
|
||||
<CancelButton
|
||||
id="btn-fail-remark-cancel"
|
||||
class="q-ml-auto"
|
||||
outlined
|
||||
@click="$emit('close')"
|
||||
/>
|
||||
<SaveButton
|
||||
id="btn-fail-remark-save"
|
||||
class="q-ml-sm"
|
||||
solid
|
||||
@click="submit"
|
||||
/>
|
||||
</template>
|
||||
</DialogFormContainer>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -394,8 +394,14 @@ watch(
|
|||
{
|
||||
label: $t('general.customer'),
|
||||
value:
|
||||
item.row.quotation.customerBranch.registerName ||
|
||||
`${item.row.quotation.customerBranch?.firstName || '-'} ${item.row.quotation.customerBranch?.lastName || ''}`,
|
||||
item.row.quotation.customerBranch.customer
|
||||
.customerType === 'CORP'
|
||||
? $i18n.locale === 'tha'
|
||||
? item.row.quotation.customerBranch.registerName
|
||||
: item.row.quotation.customerBranch.registerNameEN
|
||||
: $i18n.locale === 'tha'
|
||||
? `${item.row.quotation.customerBranch?.firstName || '-'} ${item.row.quotation.customerBranch?.lastName || ''}`
|
||||
: `${item.row.quotation.customerBranch?.firstNameEN || '-'} ${item.row.quotation.customerBranch?.lastNameEN || ''}`,
|
||||
},
|
||||
{
|
||||
label: $t('taskOrder.issueDate'),
|
||||
|
|
|
|||
|
|
@ -83,11 +83,16 @@ defineEmits<{
|
|||
<!-- NOTE: custom column will starts with # -->
|
||||
<template v-if="!col.name.startsWith('#')">
|
||||
<span>
|
||||
{{
|
||||
typeof col.field === 'string'
|
||||
? props.row[col.field as keyof Invoice]
|
||||
: col.field(props.row)
|
||||
}}
|
||||
<template v-if="typeof col.field === 'function'">
|
||||
{{
|
||||
typeof col.field(props.row) === 'object'
|
||||
? col.field(props.row)[$i18n.locale]
|
||||
: col.field(props.row)
|
||||
}}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ props.row[col.field as keyof Invoice] }}
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
<template v-if="col.name === '#order'">
|
||||
|
|
|
|||
|
|
@ -29,7 +29,16 @@ export const columns = [
|
|||
name: 'customer',
|
||||
align: 'center',
|
||||
label: 'general.customer',
|
||||
field: (v: Invoice) => v.quotation.customerBranch.registerName,
|
||||
field: (v: Invoice) =>
|
||||
v.quotation.customerBranch.customer.customerType === 'CORP'
|
||||
? {
|
||||
tha: v.quotation.customerBranch.registerName,
|
||||
eng: v.quotation.customerBranch.registerNameEN,
|
||||
}
|
||||
: {
|
||||
tha: `${v.quotation.customerBranch.firstName || ''} ${v.quotation.customerBranch.lastName || ''}`,
|
||||
eng: `${v.quotation.customerBranch.firstNameEN || ''} ${v.quotation.customerBranch.lastNameEN || ''}`,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -134,6 +134,11 @@ onMounted(async () => {
|
|||
|
||||
creditNote.getCreditNoteStats().then((res) => res && (stats.value = res));
|
||||
getList();
|
||||
|
||||
window.addEventListener('focus', () => {
|
||||
creditNote.getCreditNoteStats().then((res) => res && (stats.value = res));
|
||||
getList();
|
||||
});
|
||||
});
|
||||
|
||||
watch(
|
||||
|
|
@ -406,10 +411,12 @@ watch(
|
|||
value:
|
||||
item.row.quotation.customerBranch.customer
|
||||
.customerType === 'CORP'
|
||||
? item.row.quotation.customerBranch.registerName
|
||||
? $i18n.locale === 'tha'
|
||||
? item.row.quotation.customerBranch.registerName
|
||||
: item.row.quotation.customerBranch.registerNameEN
|
||||
: $i18n.locale === 'tha'
|
||||
? `${item.row.quotation.customerBranch.firstName} ${item.row.quotation.customerBranch.lastName}`
|
||||
: `${item.row.quotation.customerBranch.firstNameEN} ${item.row.quotation.customerBranch.lastNameEN}`,
|
||||
? `${item.row.quotation.customerBranch.firstName || ''} ${item.row.quotation.customerBranch.lastName || ''}`
|
||||
: `${item.row.quotation.customerBranch.firstNameEN || ''} ${item.row.quotation.customerBranch.lastNameEN || ''}`,
|
||||
},
|
||||
{
|
||||
label: $t('requestList.quotationCode'),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { columns } from './constants';
|
|||
import KebabAction from 'src/components/shared/KebabAction.vue';
|
||||
|
||||
const creditNote = useCreditNote();
|
||||
const { data, page } = storeToRefs(creditNote);
|
||||
const { data, page, pageSize } = storeToRefs(creditNote);
|
||||
|
||||
const prop = defineProps<{
|
||||
grid: boolean;
|
||||
|
|
@ -26,7 +26,14 @@ const visible = computed(() =>
|
|||
<template>
|
||||
<q-table
|
||||
:rows-per-page-options="[0]"
|
||||
:rows="data.map((item, i) => ({ ...item, _index: i, _page: page }))"
|
||||
:rows="
|
||||
data.map((item, i) => ({
|
||||
...item,
|
||||
_index: i,
|
||||
_page: page,
|
||||
_pageSize: pageSize,
|
||||
}))
|
||||
"
|
||||
:columns="visible"
|
||||
:grid
|
||||
hide-bottom
|
||||
|
|
@ -52,7 +59,7 @@ const visible = computed(() =>
|
|||
|
||||
<template
|
||||
v-slot:body="props: {
|
||||
row: CreditNote & { _index: number; _page: number };
|
||||
row: CreditNote & { _index: number; _page: number; _pageSize: number };
|
||||
} & Omit<Parameters<QTableSlots['body']>[0], 'row'>"
|
||||
>
|
||||
<q-tr :class="{ dark: $q.dark.isActive }" class="text-center">
|
||||
|
|
|
|||
|
|
@ -32,8 +32,9 @@ export const columns = [
|
|||
name: 'order',
|
||||
align: 'center',
|
||||
label: 'general.order',
|
||||
field: (data: CreditNote & { _index: number; _page: number }) =>
|
||||
data._page * (data._index + 1),
|
||||
field: (
|
||||
data: CreditNote & { _index: number; _page: number; _pageSize: number },
|
||||
) => (data._page - 1) * data._pageSize + (data._index + 1),
|
||||
},
|
||||
{
|
||||
name: 'code',
|
||||
|
|
|
|||
|
|
@ -209,47 +209,7 @@ const selectedWorker = ref<
|
|||
}[];
|
||||
})[]
|
||||
>([]);
|
||||
const selectedWorkerItem = computed(() => {
|
||||
return [
|
||||
...selectedWorker.value.map((e) => ({
|
||||
foreignRefNo: e.employeePassport
|
||||
? e.employeePassport[0]?.number || '-'
|
||||
: '-',
|
||||
employeeName:
|
||||
locale.value === Lang.English
|
||||
? `${e.firstNameEN} ${e.lastNameEN}`
|
||||
: `${e.firstName || e.firstNameEN} ${e.lastName || e.lastNameEN}`,
|
||||
birthDate: dateFormatJS({ date: e.dateOfBirth }),
|
||||
gender: e.gender,
|
||||
age: calculateAge(e.dateOfBirth),
|
||||
nationality: optionStore.mapOption(e.nationality),
|
||||
documentExpireDate:
|
||||
e.employeePassport !== undefined &&
|
||||
e.employeePassport[0]?.expireDate !== undefined
|
||||
? dateFormatJS({ date: e.employeePassport[0]?.expireDate })
|
||||
: '-',
|
||||
imgUrl: e.selectedImage
|
||||
? `${API_BASE_URL}/employee/${e.id}/image/${e.selectedImage}`
|
||||
: '',
|
||||
status: e.status,
|
||||
})),
|
||||
|
||||
...newWorkerList.value.map((v: any) => ({
|
||||
foreignRefNo: v.passportNo || '-',
|
||||
employeeName:
|
||||
locale.value === Lang.English
|
||||
? `${v.firstNameEN} ${v.lastNameEN}`
|
||||
: `${v.firstName || v.firstNameEN} ${v.lastName || v.lastNameEN}`,
|
||||
birthDate: dateFormatJS({ date: v.dateOfBirth }),
|
||||
gender: v.gender,
|
||||
age: calculateAge(v.dateOfBirth),
|
||||
nationality: optionStore.mapOption(v.nationality),
|
||||
documentExpireDate: '-',
|
||||
imgUrl: '',
|
||||
status: 'CREATED',
|
||||
})),
|
||||
];
|
||||
});
|
||||
const selectedWorkerItem = ref([]);
|
||||
|
||||
const selectedInstallmentNo = ref<number[]>([]);
|
||||
const installmentAmount = ref<number>(0);
|
||||
|
|
@ -374,7 +334,39 @@ function assignProductServiceList() {
|
|||
|
||||
function assignSelectedWorker() {
|
||||
if (debitNoteData.value)
|
||||
selectedWorker.value = debitNoteData.value.worker.map((v) => v.employee);
|
||||
selectedWorkerItem.value = debitNoteData.value.worker.map((e) => {
|
||||
return {
|
||||
id: e.employee.id,
|
||||
foreignRefNo: e.employee.employeePassport
|
||||
? e.employee.employeePassport[0]?.number || '-'
|
||||
: '-',
|
||||
employeeName:
|
||||
locale.value === Lang.English
|
||||
? `${e.employee.firstNameEN} ${e.employee.lastNameEN}`
|
||||
: `${e.employee.firstName || e.employee.firstNameEN} ${e.employee.lastName || e.employee.lastNameEN}`,
|
||||
birthDate: dateFormatJS({ date: e.employee.dateOfBirth }),
|
||||
gender: e.employee.gender,
|
||||
age: calculateAge(e.employee.dateOfBirth),
|
||||
nationality: optionStore.mapOption(e.employee.nationality),
|
||||
documentExpireDate:
|
||||
e.employee.employeePassport !== undefined &&
|
||||
e.employee.employeePassport[0]?.expireDate !== undefined
|
||||
? dateFormatJS({ date: e.employee.employeePassport[0]?.expireDate })
|
||||
: '-',
|
||||
imgUrl: e.employee.selectedImage
|
||||
? `${API_BASE_URL}/employee/${e.id}/image/${e.employee.selectedImage}`
|
||||
: '',
|
||||
status: e.employee.status,
|
||||
workerNew: false,
|
||||
lastNameEN: e.employee.lastNameEN,
|
||||
lastName: e.employee.lastName,
|
||||
middleNameEN: e.employee.middleNameEN,
|
||||
middleName: e.employee.middleName,
|
||||
firstNameEN: e.employee.firstNameEN,
|
||||
firstName: e.employee.firstName,
|
||||
namePrefix: e.employee.namePrefix,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async function assignFormData(id: string) {
|
||||
|
|
@ -386,7 +378,7 @@ async function assignFormData(id: string) {
|
|||
selectedProductGroup.value =
|
||||
data.productServiceList[0]?.product.productGroup?.id || '';
|
||||
|
||||
((previousValue = {
|
||||
(previousValue = {
|
||||
id: data.id || undefined,
|
||||
debitNoteQuotationId: data.debitNoteQuotationId || undefined,
|
||||
productServiceList: structuredClone(
|
||||
|
|
@ -412,7 +404,7 @@ async function assignFormData(id: string) {
|
|||
quotationId: data.debitNoteQuotationId,
|
||||
remark: data.remark || undefined,
|
||||
}),
|
||||
(currentFormData.value = structuredClone(previousValue)));
|
||||
(currentFormData.value = structuredClone(previousValue));
|
||||
|
||||
assignProductServiceList();
|
||||
assignSelectedWorker();
|
||||
|
|
@ -540,8 +532,6 @@ function getPrice(
|
|||
) {
|
||||
if (filterHook) list = list.filter(filterHook);
|
||||
|
||||
console.log(list);
|
||||
|
||||
return list.reduce(
|
||||
(a, c) => {
|
||||
if (
|
||||
|
|
@ -815,15 +805,11 @@ async function submit() {
|
|||
|
||||
worker: JSON.parse(
|
||||
JSON.stringify([
|
||||
...selectedWorker.value.map((v) => {
|
||||
...selectedWorkerItem.value.map((v) => {
|
||||
{
|
||||
return v.id;
|
||||
}
|
||||
}),
|
||||
...newWorkerList.value.map((v) => {
|
||||
const { attachment, ...payload } = v;
|
||||
return pageState.mode === 'edit' ? payload.id : payload;
|
||||
}),
|
||||
]),
|
||||
),
|
||||
dueDate: currentFormData.value.dueDate,
|
||||
|
|
@ -902,7 +888,7 @@ function storeDataLocal() {
|
|||
dueDate: currentFormData.value.dueDate,
|
||||
},
|
||||
productServicelist: productService.value,
|
||||
selectedWorker: selectedWorker.value,
|
||||
selectedWorker: selectedWorkerItem.value,
|
||||
createdBy: getName(),
|
||||
},
|
||||
}),
|
||||
|
|
@ -927,6 +913,69 @@ function closeAble() {
|
|||
return window.opener !== null;
|
||||
}
|
||||
|
||||
function combineWorker(newWorker: any, oldWorker: any) {
|
||||
selectedWorkerItem.value = [
|
||||
...oldWorker.map((e) => ({
|
||||
id: e.id,
|
||||
foreignRefNo: e.employeePassport
|
||||
? e.employeePassport[0]?.number || '-'
|
||||
: '-',
|
||||
employeeName:
|
||||
locale.value === Lang.English
|
||||
? `${e.firstNameEN} ${e.lastNameEN}`
|
||||
: `${e.firstName || e.firstNameEN} ${e.lastName || e.lastNameEN}`,
|
||||
birthDate: dateFormatJS({ date: e.dateOfBirth }),
|
||||
gender: e.gender,
|
||||
age: calculateAge(e.dateOfBirth),
|
||||
nationality: optionStore.mapOption(e.nationality),
|
||||
documentExpireDate:
|
||||
e.employeePassport !== undefined &&
|
||||
e.employeePassport[0]?.expireDate !== undefined
|
||||
? dateFormatJS({ date: e.employeePassport[0]?.expireDate })
|
||||
: '-',
|
||||
imgUrl: e.selectedImage
|
||||
? `${API_BASE_URL}/employee/${e.id}/image/${e.selectedImage}`
|
||||
: '',
|
||||
status: e.status,
|
||||
workerNew: false,
|
||||
lastNameEN: e.lastNameEN,
|
||||
lastName: e.lastName,
|
||||
middleNameEN: e.middleNameEN,
|
||||
middleName: e.middleName,
|
||||
firstNameEN: e.firstNameEN,
|
||||
firstName: e.firstName,
|
||||
namePrefix: e.namePrefix,
|
||||
})),
|
||||
|
||||
...newWorker.map((v: any) => ({
|
||||
id: v.id,
|
||||
foreignRefNo: v.passportNo || '-',
|
||||
employeeName:
|
||||
locale.value === Lang.English
|
||||
? `${v.firstNameEN} ${v.lastNameEN}`
|
||||
: `${v.firstName || v.firstNameEN} ${v.lastName || v.lastNameEN}`,
|
||||
birthDate: dateFormatJS({ date: v.dateOfBirth }),
|
||||
gender: v.gender,
|
||||
age: calculateAge(v.dateOfBirth),
|
||||
nationality: optionStore.mapOption(v.nationality),
|
||||
documentExpireDate: '-',
|
||||
imgUrl: '',
|
||||
status: 'CREATED',
|
||||
|
||||
lastNameEN: v.lastNameEN,
|
||||
lastName: v.lastName,
|
||||
middleNameEN: v.middleNameEN,
|
||||
middleName: v.middleName,
|
||||
firstNameEN: v.firstNameEN,
|
||||
firstName: v.firstName,
|
||||
namePrefix: v.namePrefix,
|
||||
|
||||
dateOfBirth: v.dateOfBirth,
|
||||
workerNew: true,
|
||||
})),
|
||||
];
|
||||
}
|
||||
|
||||
watch(
|
||||
() => pageState.mode,
|
||||
() => toggleMode(pageState.mode),
|
||||
|
|
@ -1090,6 +1139,7 @@ async function submitAccepted() {
|
|||
<PaymentForm
|
||||
v-if="view === QuotationStatus.PaymentPending"
|
||||
is-debit-note
|
||||
:branch-id="quotationData?.registeredBranchId"
|
||||
:readonly="isRoleInclude(['sale', 'head_of_sale'])"
|
||||
:data="debitNoteData"
|
||||
@fetch-status="
|
||||
|
|
@ -1111,7 +1161,7 @@ async function submitAccepted() {
|
|||
"
|
||||
:row-worker="selectedWorkerItem"
|
||||
@add-worker="() => (pageState.employeeModal = true)"
|
||||
@delete="(i) => deleteItem(selectedWorker, i)"
|
||||
@delete="(i) => deleteItem(selectedWorkerItem, i)"
|
||||
/>
|
||||
|
||||
<!-- #TODO add openProductDialog at @add-product-->
|
||||
|
|
@ -1331,7 +1381,7 @@ async function submitAccepted() {
|
|||
<SaveButton
|
||||
v-if="pageState.mode !== 'info'"
|
||||
:disabled="
|
||||
selectedWorkerItem.length === 0 && productService.length === 0
|
||||
selectedWorkerItem.length === 0 || productService.length === 0
|
||||
"
|
||||
@click="submit"
|
||||
solid
|
||||
|
|
@ -1376,13 +1426,12 @@ async function submitAccepted() {
|
|||
<!-- add employee quotation -->
|
||||
|
||||
<QuotationFormWorkerSelect
|
||||
:preselect-worker="selectedWorker"
|
||||
:preselect-worker="selectedWorkerItem"
|
||||
:customerBranchId="quotationData?.customerBranchId"
|
||||
v-model:open="pageState.employeeModal"
|
||||
v-model:new-worker-list="newWorkerList"
|
||||
@success="
|
||||
(v) => {
|
||||
selectedWorker = v.worker;
|
||||
combineWorker(v.newWorker, v.worker);
|
||||
}
|
||||
"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -143,6 +143,15 @@ onMounted(async () => {
|
|||
});
|
||||
|
||||
getList();
|
||||
|
||||
window.addEventListener('focus', () => {
|
||||
debitNote.getDebitNoteStats().then((res) => {
|
||||
if (res) {
|
||||
stats.value = res;
|
||||
}
|
||||
});
|
||||
getList();
|
||||
});
|
||||
});
|
||||
|
||||
watch(
|
||||
|
|
@ -425,10 +434,12 @@ watch(
|
|||
label: $t('quotation.customer'),
|
||||
value:
|
||||
item.row.customerBranch.customer.customerType === 'CORP'
|
||||
? item.row.customerBranch.registerName
|
||||
? $i18n.locale === 'tha'
|
||||
? item.row.customerBranch.registerName
|
||||
: item.row.customerBranch.registerNameEN
|
||||
: $i18n.locale === 'tha'
|
||||
? `${item.row.customerBranch.firstName} ${item.row.customerBranch.lastName}`
|
||||
: `${item.row.customerBranch.firstNameEN} ${item.row.customerBranch.lastNameEN}`,
|
||||
? `${item.row.customerBranch.firstName || ''} ${item.row.customerBranch.lastName || ''}`
|
||||
: `${item.row.customerBranch.firstNameEN || ''} ${item.row.customerBranch.lastNameEN || ''}`,
|
||||
},
|
||||
{
|
||||
label: $t('requestList.quotationCode'),
|
||||
|
|
|
|||
|
|
@ -343,9 +343,16 @@ watch(
|
|||
{
|
||||
label: $t('general.customer'),
|
||||
value:
|
||||
item.row.invoice.quotation.customerBranch
|
||||
.registerName ||
|
||||
`${item.row.invoice.quotation.customerBranch?.firstName || '-'} ${item.row.invoice.quotation.customerBranch?.lastName || ''}`,
|
||||
item.row.invoice.quotation.customerBranch.customer
|
||||
.customerType === 'CORP'
|
||||
? $i18n.locale === 'tha'
|
||||
? item.row.invoice.quotation.customerBranch
|
||||
.registerName
|
||||
: item.row.invoice.quotation.customerBranch
|
||||
.registerNameEN
|
||||
: $i18n.locale === 'tha'
|
||||
? `${item.row.invoice.quotation.customerBranch?.firstName || '-'} ${item.row.invoice.quotation.customerBranch?.lastName || ''}`
|
||||
: `${item.row.invoice.quotation.customerBranch?.firstNameEN || '-'} ${item.row.invoice.quotation.customerBranch?.lastNameEN || ''}`,
|
||||
},
|
||||
{
|
||||
label: $t('taskOrder.issueDate'),
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ const fieldSelected = ref<('no' | 'name' | 'nameEN')[]>([
|
|||
const fieldSelectedOption = ref<{ label: string; value: string }[]>([
|
||||
{
|
||||
label: 'general.order',
|
||||
value: 'orderNumber',
|
||||
value: 'no',
|
||||
},
|
||||
|
||||
{
|
||||
|
|
@ -312,25 +312,6 @@ watch(
|
|||
</q-input>
|
||||
|
||||
<div class="row col-md-5 justify-end" style="white-space: nowrap">
|
||||
<q-select
|
||||
v-if="$q.screen.gt.sm"
|
||||
v-model="statusFilter"
|
||||
outlined
|
||||
dense
|
||||
option-value="value"
|
||||
option-label="label"
|
||||
class="col"
|
||||
:class="{ 'offset-md-5': pageState.gridView }"
|
||||
map-options
|
||||
emit-value
|
||||
:for="'field-select-status'"
|
||||
:hide-dropdown-icon="$q.screen.lt.sm"
|
||||
:options="[
|
||||
{ label: $t('general.all'), value: 'all' },
|
||||
{ label: $t('general.active'), value: 'statusACTIVE' },
|
||||
{ label: $t('general.inactive'), value: 'statusINACTIVE' },
|
||||
]"
|
||||
/>
|
||||
<q-select
|
||||
v-if="!pageState.gridView"
|
||||
id="select-field"
|
||||
|
|
@ -595,10 +576,18 @@ watch(
|
|||
></q-badge>
|
||||
</q-avatar>
|
||||
<span class="text-weight-bold column q-pl-md">
|
||||
{{ props.row.name }}
|
||||
{{
|
||||
$i18n.locale === 'tha'
|
||||
? props.row.name
|
||||
: props.row.nameEN
|
||||
}}
|
||||
|
||||
<span class="text-caption app-text-muted-2">
|
||||
{{ props.row.nameEN }}
|
||||
{{
|
||||
$i18n.locale === 'tha'
|
||||
? props.row.nameEN
|
||||
: props.row.name
|
||||
}}
|
||||
</span>
|
||||
</span>
|
||||
<nav
|
||||
|
|
|
|||
|
|
@ -447,6 +447,17 @@ const useBranchStore = defineStore('api-branch', () => {
|
|||
return false;
|
||||
}
|
||||
|
||||
async function fetchListBankByBranch(branchId: string) {
|
||||
const res = await api.get(`/branch/${branchId}/bank`, {
|
||||
headers: { 'X-Rtid': flowStore.rtid },
|
||||
});
|
||||
|
||||
if (!res) return false;
|
||||
if (res.status === 200) return res.data;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
map,
|
||||
|
|
@ -475,6 +486,8 @@ const useBranchStore = defineStore('api-branch', () => {
|
|||
fetchByIdAttachment,
|
||||
putAttachment,
|
||||
deleteByIdAttachment,
|
||||
|
||||
fetchListBankByBranch,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { defineStore } from 'pinia';
|
|||
import { api } from 'src/boot/axios';
|
||||
|
||||
import { Pagination } from 'stores/types';
|
||||
import { getToken } from 'src/services/keycloak';
|
||||
|
||||
import {
|
||||
ProductGroup,
|
||||
|
|
@ -19,6 +20,7 @@ import {
|
|||
import { ref } from 'vue';
|
||||
|
||||
const useProductServiceStore = defineStore('api-product-service', () => {
|
||||
const baseUrl = import.meta.env.VITE_API_BASE_URL;
|
||||
const splitPay = ref<number>(0);
|
||||
const workNameItems = ref<
|
||||
{
|
||||
|
|
@ -528,6 +530,53 @@ const useProductServiceStore = defineStore('api-product-service', () => {
|
|||
if (!res) return false;
|
||||
}
|
||||
|
||||
async function productExport(params: {
|
||||
status?: 'CREATED' | 'ACTIVE' | 'INACTIVE';
|
||||
shared?: boolean;
|
||||
productGroupId?: string;
|
||||
query?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
orderField?: string;
|
||||
orderBy?: string;
|
||||
activeOnly?: boolean;
|
||||
startDate?: Date;
|
||||
endDate?: Date;
|
||||
}) {
|
||||
let url = baseUrl + '/' + 'product-export';
|
||||
|
||||
const queryParams = new URLSearchParams(
|
||||
Object.keys(params).reduce((acc: Record<string, string>, key) => {
|
||||
const value = params[key as keyof typeof params];
|
||||
if (value !== undefined && value !== null && value !== '') {
|
||||
const stringValue =
|
||||
typeof value === 'boolean' || typeof value === 'number'
|
||||
? String(value)
|
||||
: value instanceof Date
|
||||
? value.toISOString()
|
||||
: String(value);
|
||||
acc[key] = stringValue;
|
||||
}
|
||||
return acc;
|
||||
}, {}),
|
||||
);
|
||||
|
||||
url += '?' + queryParams.toString();
|
||||
|
||||
const res = await fetch(url, {
|
||||
headers: { ['Authorization']: 'Bearer ' + (await getToken()) },
|
||||
});
|
||||
const text = await res.json();
|
||||
const blob = new Blob([text], { type: 'text/csv' });
|
||||
if (res.ok && blob) {
|
||||
const a = document.createElement('a');
|
||||
a.download = 'customer-report' + '.csv';
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.click();
|
||||
a.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
workNameItems,
|
||||
splitPay,
|
||||
|
|
@ -568,6 +617,8 @@ const useProductServiceStore = defineStore('api-product-service', () => {
|
|||
deleteImageByName,
|
||||
|
||||
importProduct,
|
||||
|
||||
productExport,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -23,22 +23,23 @@ const templates = {
|
|||
installments?: {
|
||||
no: number;
|
||||
amount: number;
|
||||
name?: string;
|
||||
}[];
|
||||
}) => {
|
||||
if (context?.paymentType === 'Full') {
|
||||
return [
|
||||
'**** เงื่อนไขเพิ่มเติม',
|
||||
'- เงื่อนไขการชำระเงิน แบบเต็มจำนวน',
|
||||
` จำนวน ${formatNumberDecimal(context?.amount || 0, 2)}`,
|
||||
`**** ${i18n.global.t('general.additional')}`,
|
||||
`- ${i18n.global.t('quotation.paymentCondition')} ${i18n.global.t('quotation.type.Full')}`,
|
||||
` ${i18n.global.t('general.amount')} ${formatNumberDecimal(context?.amount || 0, 2)}`,
|
||||
].join('<br/>');
|
||||
} else {
|
||||
return [
|
||||
'**** เงื่อนไขเพิ่มเติม',
|
||||
`- เงื่อนไขการชำระเงิน แบบแบ่งจ่าย${context?.paymentType === 'SplitCustom' ? ' กำหนดเอง ' : ' '}${context?.installments?.length} งวด`,
|
||||
...(context?.installments?.map(
|
||||
(v) =>
|
||||
` งวดที่ ${v.no} จำนวน ${formatNumberDecimal(v.amount, 2)}`,
|
||||
) || []),
|
||||
`**** ${i18n.global.t('general.additional')}`,
|
||||
`- ${i18n.global.t('quotation.paymentCondition')} ${i18n.global.t('quotation.type.Split')}${context?.paymentType === 'SplitCustom' ? ` (${i18n.global.t('general.specify')}) ` : ' '}${context?.installments?.length} ${i18n.global.t('quotation.receiptDialog.installments')}`,
|
||||
...(context?.installments?.map((v) => {
|
||||
const installmentName = v.name ? ` (${v.name})` : '';
|
||||
return ` ${i18n.global.t('quotation.periodNo')} ${v.no}${installmentName} ${i18n.global.t('general.amount')} ${formatNumberDecimal(v.amount, 2)}`;
|
||||
}) || []),
|
||||
].join('<br />');
|
||||
}
|
||||
},
|
||||
|
|
@ -73,14 +74,14 @@ const templates = {
|
|||
const employee = v.request.employee;
|
||||
const branch = v.request.quotation.customerBranch;
|
||||
return (
|
||||
`${i + 1}. ` +
|
||||
` ${v.request.code}_${branch.customer.customerType === 'PERS' ? `${branch.namePrefix}. ${branch.firstNameEN} ${branch.lastNameEN} `.toUpperCase() : branch.registerName}_` +
|
||||
` ${i + 1}. ` +
|
||||
` ${v.request.code}_${branch.customer.customerType === 'PERS' ? `${branch.namePrefix}. ${branch.firstNameEN} ${branch.lastNameEN} `.toUpperCase() : i18n.global.locale.value == 'tha' ? branch.registerName : branch.registerNameEN}_` +
|
||||
`${employee.namePrefix}. ${employee.firstNameEN} ${employee.lastNameEN} `.toUpperCase() +
|
||||
`${!!v.failedType && v.failedType !== 'other' ? `${i18n.global.t(`taskOrder.${v.failedType}`)}` : !!v.failedComment ? v.failedComment : ''}`
|
||||
);
|
||||
});
|
||||
return [
|
||||
`- ${item.product.name} ราคา ${price} บาท`,
|
||||
`- ${item.product.name} ${i18n.global.t('price', { price: price })} `,
|
||||
'',
|
||||
...list,
|
||||
'',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue