Merge branch 'develop'
|
Before Width: | Height: | Size: 151 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 7.4 KiB |
1656
src/components/03_customer-management/DialogEmployee.vue
Normal file
1772
src/components/03_customer-management/DrawerEmployee.vue
Normal file
|
|
@ -20,8 +20,8 @@ const issuePlace = defineModel<string>('issuePlace');
|
|||
const issueCountry = defineModel<string>('issueCountry');
|
||||
const issueDate = defineModel<Date | null | string>('issueDate');
|
||||
const type = defineModel<string>('type');
|
||||
const expireDate = defineModel<Date>('expireDate');
|
||||
const birthDate = defineModel<Date>('birthDate');
|
||||
const expireDate = defineModel<Date | string>('expireDate');
|
||||
const birthDate = defineModel<Date | string>('birthDate');
|
||||
const workerStatus = defineModel<string>('workerStatus');
|
||||
const nationality = defineModel<string>('nationality');
|
||||
const gender = defineModel<string>('gender');
|
||||
|
|
|
|||
|
|
@ -28,12 +28,12 @@ const arrivalAt = defineModel<string>('arrivalAt');
|
|||
const arrivalTMNo = defineModel<string>('arrivalTmNo');
|
||||
const arrivalTM = defineModel<string>('arrivalTm');
|
||||
const mrz = defineModel<string>('mrz');
|
||||
const entryCount = defineModel<number>('entryCount');
|
||||
const entryCount = defineModel<number | string>('entryCount');
|
||||
const issuePlace = defineModel<string>('issuePlace');
|
||||
const issueCountry = defineModel<string>('issueCountry');
|
||||
const issueDate = defineModel<Date | null | string>('visaIssueDate');
|
||||
const type = defineModel<string>('type');
|
||||
const expireDate = defineModel<Date>('expireDate');
|
||||
const expireDate = defineModel<Date | string>('expireDate');
|
||||
const remark = defineModel<string>('remark');
|
||||
const workerType = defineModel<string>('workerType');
|
||||
const number = defineModel<string>('number');
|
||||
|
|
|
|||
|
|
@ -141,8 +141,9 @@ defineEmits<{
|
|||
<q-avatar size="md">
|
||||
<q-img
|
||||
:src="
|
||||
`${baseUrl}/employee/${props.row.id}/image/${props.row.selectedImage}` ||
|
||||
`/images/employee-avatar-${props.row.gender}.png`
|
||||
props.row.selectedImage
|
||||
? `${baseUrl}/employee/${props.row.id}/image/${props.row.selectedImage}`
|
||||
: `/images/employee-avatar-${props.row.gender}.png`
|
||||
"
|
||||
class="text-center"
|
||||
:ratio="1"
|
||||
|
|
@ -295,9 +296,9 @@ defineEmits<{
|
|||
$i18n.locale === 'eng'
|
||||
? `${props.row.firstNameEN} ${props.row.lastNameEN} `.trim()
|
||||
: `${props.row.firstName} ${props.row.lastName} `.trim(),
|
||||
img:
|
||||
`${baseUrl}/employee/${props.row.id}/image/${props.row.selectedImage}` ||
|
||||
`/images/employee-avatar-${props.row.gender}.png`,
|
||||
img: props.row.selectedImage
|
||||
? `${baseUrl}/employee/${props.row.id}/image/${props.row.selectedImage}`
|
||||
: `/images/employee-avatar-${props.row.gender}.png`,
|
||||
fallbackImg: `/images/employee-avatar-${props.row.gender}.png`,
|
||||
male: props.row.gender === 'male',
|
||||
female: props.row.gender === 'female',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { CustomerBranch } from 'src/stores/customer';
|
||||
|
||||
import SelectCustomer from '../shared/select/SelectCustomer.vue';
|
||||
import SelectBranch from '../shared/select/SelectBranch.vue';
|
||||
|
||||
|
|
@ -7,6 +9,10 @@ const customerBranchId = defineModel<string>('customerBranchId');
|
|||
const agentPrice = defineModel<boolean>('agentPrice');
|
||||
const special = defineModel<boolean>('special');
|
||||
|
||||
const customerBranchOption = defineModel<CustomerBranch>(
|
||||
'customerBranchOption',
|
||||
);
|
||||
|
||||
defineProps<{
|
||||
outlined?: boolean;
|
||||
readonly?: boolean;
|
||||
|
|
@ -71,6 +77,7 @@ defineEmits<{
|
|||
id="about-select-customer-branch-id"
|
||||
for="about-select-customer-branch-id"
|
||||
v-model:value="customerBranchId"
|
||||
v-model:value-option="customerBranchOption"
|
||||
:label="$t('quotation.customer')"
|
||||
:creatable-disabled-text="`(${$t('form.error.selectField', {
|
||||
field: $t('quotation.branchVirtual'),
|
||||
|
|
|
|||
|
|
@ -58,16 +58,15 @@ const currentBtnOpen = ref<{ title: string; opened: boolean[] }[]>([
|
|||
|
||||
function calcPrice(c: (typeof rows.value)[number]) {
|
||||
const originalPrice = c.pricePerUnit;
|
||||
const finalPriceWithVat = precisionRound(
|
||||
originalPrice + originalPrice * (config.value?.vat || 0.07),
|
||||
const finalPricePerUnit = precisionRound(
|
||||
originalPrice +
|
||||
(c.product[props.agentPrice ? 'agentPriceCalcVat' : 'calcVat']
|
||||
? originalPrice * (config.value?.vat || 0.07)
|
||||
: 0),
|
||||
);
|
||||
const finalPriceNoVat = finalPriceWithVat / (1 + (config.value?.vat || 0.07));
|
||||
const price = finalPricePerUnit * c.amount - c.discount;
|
||||
|
||||
const price = finalPriceNoVat * c.amount - c.discount;
|
||||
const vat = c.product[props.agentPrice ? 'agentPriceCalcVat' : 'calcVat']
|
||||
? (finalPriceNoVat * c.amount - c.discount) * (config.value?.vat || 0.07)
|
||||
: 0;
|
||||
return precisionRound(price + vat);
|
||||
return precisionRound(price);
|
||||
}
|
||||
|
||||
const discount4Show = ref<string[]>([]);
|
||||
|
|
@ -435,8 +434,20 @@ watch(
|
|||
<q-td align="right">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
props.row.pricePerUnit * props.row.amount -
|
||||
props.row.discount,
|
||||
props.row.product[
|
||||
agentPrice ? 'agentPriceCalcVat' : 'calcVat'
|
||||
]
|
||||
? precisionRound(
|
||||
(props.row.pricePerUnit *
|
||||
(1 + (config?.vat || 0.07)) *
|
||||
props.row.amount -
|
||||
props.row.discount) /
|
||||
(1 + (config?.vat || 0.07)),
|
||||
)
|
||||
: precisionRound(
|
||||
props.row.pricePerUnit * props.row.amount -
|
||||
props.row.discount,
|
||||
),
|
||||
2,
|
||||
)
|
||||
}}
|
||||
|
|
@ -448,9 +459,12 @@ watch(
|
|||
agentPrice ? 'agentPriceCalcVat' : 'calcVat'
|
||||
]
|
||||
? precisionRound(
|
||||
(props.row.pricePerUnit * props.row.amount -
|
||||
props.row.discount) *
|
||||
(config?.vat || 0.07),
|
||||
((props.row.pricePerUnit *
|
||||
(1 + (config?.vat || 0.07)) *
|
||||
props.row.amount -
|
||||
props.row.discount) /
|
||||
(1 + (config?.vat || 0.07))) *
|
||||
0.07,
|
||||
)
|
||||
: 0,
|
||||
2,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
UndoButton,
|
||||
} from 'components/button';
|
||||
|
||||
withDefaults(
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
title: string;
|
||||
category?: string;
|
||||
|
|
@ -42,6 +42,11 @@ const drawerOpen = defineModel<boolean>('drawerOpen', {
|
|||
const myForm = ref();
|
||||
|
||||
function reset() {
|
||||
if (props.beforeClose) {
|
||||
drawerOpen.value = props.beforeClose
|
||||
? props.beforeClose()
|
||||
: !drawerOpen.value;
|
||||
}
|
||||
if (myForm.value) {
|
||||
myForm.value.resetValidation();
|
||||
}
|
||||
|
|
@ -62,7 +67,6 @@ async function onValidationError(ref: any) {
|
|||
@show="show"
|
||||
@before-hide="reset"
|
||||
@hide="close"
|
||||
@update:model-value="(v) => (drawerOpen = beforeClose ? beforeClose() : v)"
|
||||
:width="$q.screen.gt.xs ? windowSize * 0.85 : windowSize"
|
||||
v-model="drawerOpen"
|
||||
behavior="mobile"
|
||||
|
|
|
|||
|
|
@ -16,3 +16,4 @@ export { default as SideMenu } from './SideMenu.vue';
|
|||
export { default as StatCardComponent } from './StatCardComponent.vue';
|
||||
export { default as TooltipComponent } from './TooltipComponent.vue';
|
||||
export { default as TreeComponent } from './TreeComponent.vue';
|
||||
export { default as PaginationPageSize } from './PaginationPageSize.vue';
|
||||
|
|
|
|||
|
|
@ -158,6 +158,39 @@ function setDefaultValue() {
|
|||
<q-separator class="q-mx-sm" />
|
||||
</template>
|
||||
|
||||
<template #before-options v-if="creatable">
|
||||
<q-item
|
||||
:disable="creatableDisabled"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click.stop="$emit('create')"
|
||||
for="select-business-type-add-new"
|
||||
id="select-business-type-add-new"
|
||||
>
|
||||
<q-item-section>
|
||||
<span class="row items-center">
|
||||
<q-icon
|
||||
name="mdi-plus-circle-outline"
|
||||
class="q-mr-sm"
|
||||
style="color: hsl(var(--positive-bg))"
|
||||
/>
|
||||
<b>
|
||||
{{ $t('general.add', { text: $t('menu.manage.businessType') }) }}
|
||||
</b>
|
||||
<span
|
||||
v-if="creatableDisabled && creatableDisabledText"
|
||||
class="app-text-muted q-pl-xs"
|
||||
style="font-size: 80%"
|
||||
>
|
||||
{{ creatableDisabledText }}
|
||||
</span>
|
||||
</span>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-separator class="q-mx-sm" />
|
||||
</template>
|
||||
|
||||
<template #option="{ opt, scope }">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<span class="row items-center">
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ defineEmits<{
|
|||
type ExclusiveProps = {
|
||||
simple?: boolean;
|
||||
simpleBranchNo?: boolean;
|
||||
selectFirstValue?: boolean;
|
||||
};
|
||||
|
||||
const props = defineProps<SelectProps<typeof getList> & ExclusiveProps>();
|
||||
|
|
@ -64,10 +65,14 @@ onMounted(async () => {
|
|||
setFirstValue();
|
||||
}
|
||||
|
||||
await getSelectedOption();
|
||||
|
||||
valueOption.value = selectOptions.value.find((v) => v.id === value.value);
|
||||
if (props.selectFirstValue) {
|
||||
setDefaultValue();
|
||||
} else await getSelectedOption();
|
||||
});
|
||||
|
||||
function setDefaultValue() {
|
||||
setFirstValue();
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<SelectInput
|
||||
|
|
@ -160,11 +165,9 @@ onMounted(async () => {
|
|||
</template>
|
||||
|
||||
<template #option="{ opt, scope }">
|
||||
<q-item @click="valueOption = opt" v-bind="scope.itemProps">
|
||||
<q-item v-bind="scope.itemProps" class="q-mx-sm bodrder">
|
||||
<SelectCustomerItem :data="opt" :simple :simple-branch-no />
|
||||
</q-item>
|
||||
|
||||
<q-separator class="q-mx-sm" />
|
||||
</template>
|
||||
|
||||
<template #append v-if="clearable">
|
||||
|
|
@ -177,3 +180,11 @@ onMounted(async () => {
|
|||
</template>
|
||||
</SelectInput>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.bodrder {
|
||||
border-bottom: solid;
|
||||
border-bottom-width: 1px;
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -54,10 +54,11 @@ onMounted(() => {
|
|||
@click="$emit('click')"
|
||||
>
|
||||
<q-icon :name="icon" size="lg" :style="`color: ${color}`" />
|
||||
<article class="col column q-pl-md">
|
||||
<span class="ellipsis full-width">
|
||||
<div class="col column q-pl-md">
|
||||
<div class="ellipsis full-width" style="max-width: 65vw !important">
|
||||
{{ name }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span class="text-caption app-text-muted-2">
|
||||
{{
|
||||
uploading.loaded
|
||||
|
|
@ -79,7 +80,7 @@ onMounted(() => {
|
|||
/>
|
||||
{{ idle ? `Pending` : progress !== 1 ? `Uploading...` : 'Completed' }}
|
||||
</span>
|
||||
</article>
|
||||
</div>
|
||||
<q-btn
|
||||
v-if="closeable"
|
||||
icon="mdi-close"
|
||||
|
|
|
|||
|
|
@ -45,9 +45,9 @@ const props = withDefaults(
|
|||
readonly?: boolean;
|
||||
showTitle?: boolean;
|
||||
ocr?: (
|
||||
group: any,
|
||||
group: string,
|
||||
file: File,
|
||||
) => void | Promise<{
|
||||
) => Promise<{
|
||||
status: boolean;
|
||||
group: string;
|
||||
meta: { name: string; value: string }[];
|
||||
|
|
|
|||
|
|
@ -198,3 +198,10 @@ i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-i
|
|||
.q-focus-helper {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.clear-btn {
|
||||
opacity: 0.6;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -507,6 +507,7 @@ export default {
|
|||
miss: 'MISS.',
|
||||
},
|
||||
|
||||
taxpayyerNo: 'Taxpayer Identification Number',
|
||||
citizenId: 'Citizen ID',
|
||||
religion: 'Religion',
|
||||
issueDate: 'Issue Date',
|
||||
|
|
@ -777,6 +778,8 @@ export default {
|
|||
seller: 'Seller',
|
||||
paymentChannels: 'Payment Channels',
|
||||
channelsThat: 'Channels That',
|
||||
refNo: 'Reference Number',
|
||||
bankAccount: 'Bank Account',
|
||||
bankAccountNumber: 'Bank Account Number',
|
||||
bankAccountName: 'Bank Account Name',
|
||||
inTheNameOf: 'In The Name Of',
|
||||
|
|
|
|||
|
|
@ -508,6 +508,7 @@ export default {
|
|||
religion: 'ศาสนา',
|
||||
issueDate: 'วันที่ออกหนังสือ',
|
||||
passportExpiryDate: 'วันหiมดอายุหนังสือเดินทาง',
|
||||
taxpayyerNo: 'เลขที่ประจำตัวผู้เสียภาษี',
|
||||
|
||||
ownerName: 'ชื่อนายจ้าง',
|
||||
firstName: 'ชื่อ ',
|
||||
|
|
@ -775,6 +776,8 @@ export default {
|
|||
seller: 'ผู้ขาย',
|
||||
paymentChannels: 'ช่องทางชำระเงิน',
|
||||
channelsThat: 'ช่องทางที่',
|
||||
refNo: 'เลขที่อ้างอิง',
|
||||
bankAccount: 'บัญชีธนาคาร',
|
||||
bankAccountNumber: 'เลขบัญชีธนาคาร',
|
||||
bankAccountName: 'ชื่อบัญชี',
|
||||
inTheNameOf: 'ในนาม',
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import { CustomerBranch, CustomerType } from 'stores/customer/types';
|
|||
import { columnsEmployee } from './constant';
|
||||
import { useCustomerBranchForm, useEmployeeForm } from './form';
|
||||
|
||||
import DialogEmployee from 'src/components/03_customer-management/DialogEmployee.vue';
|
||||
import DrawerEmployee from 'src/components/03_customer-management/DrawerEmployee.vue';
|
||||
import EmployerFormAuthorized from './components/employer/EmployerFormAuthorized.vue';
|
||||
import FloatingActionButton from 'components/FloatingActionButton.vue';
|
||||
import SideMenu from 'components/SideMenu.vue';
|
||||
|
|
@ -89,6 +91,11 @@ const prop = withDefaults(
|
|||
currentCitizenId?: string;
|
||||
gender: string;
|
||||
selectedImage: string;
|
||||
fetchImageList: (
|
||||
id: string,
|
||||
selectedName: string,
|
||||
type: 'customer' | 'employee',
|
||||
) => Promise<void>;
|
||||
}>(),
|
||||
{
|
||||
color: 'green',
|
||||
|
|
@ -96,7 +103,6 @@ const prop = withDefaults(
|
|||
);
|
||||
const currentBranchEmployee = ref<string>('');
|
||||
const listEmployee = ref<Employee[]>([]);
|
||||
|
||||
const customerId = defineModel<string>('customerId', { required: true });
|
||||
|
||||
defineEmits<{
|
||||
|
|
@ -106,16 +112,6 @@ defineEmits<{
|
|||
(e: 'dialog'): void;
|
||||
}>();
|
||||
|
||||
onMounted(async () => {
|
||||
customerBranchFormState.value.currentCustomerId = route.params
|
||||
.customerId as string;
|
||||
await fetchList();
|
||||
|
||||
branch.value?.forEach((v) => {
|
||||
currentBtnOpen.value.push(false);
|
||||
});
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: 'branchName',
|
||||
|
|
@ -257,10 +253,6 @@ async function fetchEmployee(opts: { branchId: string; pageSize?: number }) {
|
|||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchList();
|
||||
});
|
||||
|
||||
watch([customerId, inputSearch, currentStatus, pageSizeBranch], async () => {
|
||||
await fetchList();
|
||||
});
|
||||
|
|
@ -280,6 +272,16 @@ watch(
|
|||
}
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
customerBranchFormState.value.currentCustomerId = route.params
|
||||
.customerId as string;
|
||||
await fetchList();
|
||||
|
||||
branch.value?.forEach((v) => {
|
||||
currentBtnOpen.value.push(false);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -473,7 +475,6 @@ watch(
|
|||
<q-tr
|
||||
:class="{
|
||||
'app-text-muted': props.row.status === 'INACTIVE',
|
||||
'cursor-pointer': props.row._count?.branch !== 0,
|
||||
}"
|
||||
:props="props"
|
||||
@click="$emit('viewDetail', props.row, props.rowIndex)"
|
||||
|
|
@ -547,7 +548,13 @@ watch(
|
|||
v-if="branchFieldSelected.includes('businessTypePure')"
|
||||
class="text-left"
|
||||
>
|
||||
{{ useOptionStore().mapOption(props.row.businessType) || '-' }}
|
||||
{{
|
||||
props.row.businessType
|
||||
? props.row.businessType[
|
||||
$i18n.locale === 'eng' ? 'nameEN' : 'name'
|
||||
]
|
||||
: '-'
|
||||
}}
|
||||
</q-td>
|
||||
<q-td
|
||||
v-if="branchFieldSelected.includes('totalEmployee')"
|
||||
|
|
@ -566,8 +573,6 @@ watch(
|
|||
await fetchEmployee({
|
||||
branchId: currentBranchEmployee,
|
||||
pageSize: 999,
|
||||
passport: true,
|
||||
visa: true,
|
||||
});
|
||||
|
||||
currentBtnOpen.map((v, i) => {
|
||||
|
|
@ -638,10 +643,15 @@ watch(
|
|||
"
|
||||
@history="(item) => {}"
|
||||
@view="
|
||||
(item) => {
|
||||
async (item) => {
|
||||
employeeFormState.drawerModal = true;
|
||||
//employeeFormState.isEmployeeEdit = true;
|
||||
employeeFormStore.assignFormDataEmployee(item.id);
|
||||
await fetchImageList(
|
||||
item.id,
|
||||
item.selectedImage || '',
|
||||
'employee',
|
||||
);
|
||||
}
|
||||
"
|
||||
/>
|
||||
|
|
@ -668,9 +678,11 @@ watch(
|
|||
? `${props.row.addressEN || ''} ${props.row.subDistrict?.nameEN || ''} ${props.row.district?.nameEN || ''} ${props.row.province?.nameEN || ''}`
|
||||
: `${props.row.address || ''} ${props.row.subDistrict?.name || ''} ${props.row.district?.name || ''} ${props.row.province?.name || ''}`,
|
||||
telephone: props.row.telephoneNo,
|
||||
businessTypePure: useOptionStore().mapOption(
|
||||
props.row.businessType,
|
||||
),
|
||||
businessTypePure: props.row.businessType
|
||||
? props.row.businessType[
|
||||
$i18n.locale === 'eng' ? 'nameEN' : 'name'
|
||||
]
|
||||
: '-',
|
||||
totalEmployee: props.row._count?.employee,
|
||||
}"
|
||||
:visible-columns="branchFieldSelected"
|
||||
|
|
@ -885,15 +897,14 @@ watch(
|
|||
</div>
|
||||
<EmployerFormBusiness
|
||||
dense
|
||||
class="q-mb-xl"
|
||||
outlined
|
||||
prefix-id="employer-branch"
|
||||
:readonly="customerBranchFormState.dialogType === 'info'"
|
||||
v-model:bussiness-type="customerBranchFormData.businessType"
|
||||
v-model:business-type-id="customerBranchFormData.businessTypeId"
|
||||
v-model:job-position="customerBranchFormData.jobPosition"
|
||||
v-model:job-description="customerBranchFormData.jobDescription"
|
||||
v-model:pay-date="customerBranchFormData.payDate"
|
||||
v-model:pay-date-e-n="customerBranchFormData.payDateEN"
|
||||
v-model:pay-date-en="customerBranchFormData.payDateEN"
|
||||
v-model:wage-rate="customerBranchFormData.wageRate"
|
||||
v-model:wage-rate-text="customerBranchFormData.wageRateText"
|
||||
/>
|
||||
|
|
@ -984,7 +995,7 @@ watch(
|
|||
v-model:email="customerBranchFormData.email"
|
||||
v-model:contact-tel="customerBranchFormData.contactTel"
|
||||
v-model:office-tel="customerBranchFormData.officeTel"
|
||||
v-model:agent="customerBranchFormData.agent"
|
||||
v-model:agent-user-id="customerBranchFormData.agentUserId"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1000,6 +1011,18 @@ watch(
|
|||
/>
|
||||
</template>
|
||||
</DialogFormContainer>
|
||||
|
||||
<DialogEmployee
|
||||
:fetch-list-employee="fetchEmployee"
|
||||
:fetch-image-list="fetchImageList"
|
||||
current-tab="employer"
|
||||
/>
|
||||
|
||||
<DrawerEmployee
|
||||
:fetch-list-employee="fetchEmployee"
|
||||
:fetch-image-list="fetchImageList"
|
||||
current-tab="employer"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
2050
src/pages/03_customer-management/TabCustomer.vue
Normal file
514
src/pages/03_customer-management/TabEmployee.vue
Normal file
|
|
@ -0,0 +1,514 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import {
|
||||
PaginationComponent,
|
||||
PaginationPageSize,
|
||||
ImageUploadDialog,
|
||||
DialogForm,
|
||||
NoData,
|
||||
} from 'src/components';
|
||||
import DialogEmployee from 'src/components/03_customer-management/DialogEmployee.vue';
|
||||
import HistoryEditComponent from 'src/components/03_customer-management/HistoryEditComponent.vue';
|
||||
import TableEmpoloyee from 'src/components/03_customer-management/TableEmpoloyee.vue';
|
||||
import DrawerEmployee from 'src/components/03_customer-management/DrawerEmployee.vue';
|
||||
|
||||
import useFlowStore from 'src/stores/flow';
|
||||
import useEmployeeStore from 'src/stores/employee';
|
||||
import { Status } from 'src/stores/types';
|
||||
import { Employee, EmployeeHistory } from 'src/stores/employee/types';
|
||||
import { baseUrl, dialog, canAccess } from 'src/stores/utils';
|
||||
import { calculateAge, toISOStringWithTimezone } from 'src/utils/datetime';
|
||||
import { useCustomerForm, useEmployeeForm } from './form';
|
||||
import { columnsEmployee } from './constant';
|
||||
|
||||
const $q = useQuasar();
|
||||
const flowStore = useFlowStore();
|
||||
const employeeStore = useEmployeeStore();
|
||||
const employeeFormStore = useEmployeeForm();
|
||||
const customerFormStore = useCustomerForm();
|
||||
const { t } = useI18n();
|
||||
const { state: employeeFormState, currentFromDataEmployee } =
|
||||
storeToRefs(employeeFormStore);
|
||||
const { deleteEmployeeById } = employeeFormStore;
|
||||
const { state: customerFormState } = storeToRefs(customerFormStore);
|
||||
|
||||
const employeeStats = defineModel('employeeStats', { default: 0 });
|
||||
|
||||
const props = defineProps<{
|
||||
currentTab: 'employee' | 'employer';
|
||||
currentStatus: Status | 'All';
|
||||
gridView: boolean;
|
||||
inputSearch: string;
|
||||
searchDate: string[];
|
||||
fieldSelected: string[];
|
||||
fetchImageList: (
|
||||
id: string,
|
||||
selectedName: string,
|
||||
type: 'customer' | 'employee',
|
||||
) => Promise<void>;
|
||||
triggerChangeStatus: (
|
||||
id: string,
|
||||
status: string,
|
||||
employeeName?: string,
|
||||
) => void;
|
||||
}>();
|
||||
|
||||
defineExpose({
|
||||
openSpecificEmployee,
|
||||
fetchListEmployee,
|
||||
toggleStatusEmployee,
|
||||
});
|
||||
|
||||
const listEmployee = ref<Employee[]>([]);
|
||||
const currentPageEmployee = ref<number>(1);
|
||||
const maxPageEmployee = ref<number>(1);
|
||||
const pageSize = ref<number>(30);
|
||||
const employeeHistoryDialog = ref(false);
|
||||
const employeeHistory = ref<EmployeeHistory[]>();
|
||||
|
||||
const splitPercent = computed(() => ($q.screen.lt.md ? 0 : 15));
|
||||
|
||||
async function fetchListEmployee(opt?: {
|
||||
fetchStats?: boolean;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
customerId?: string;
|
||||
mobileFetch?: boolean;
|
||||
}) {
|
||||
const resultListEmployee = await employeeStore.fetchList({
|
||||
customerId: opt?.customerId,
|
||||
page: opt
|
||||
? opt.mobileFetch
|
||||
? 1
|
||||
: opt.page || currentPageEmployee.value
|
||||
: currentPageEmployee.value,
|
||||
pageSize: opt
|
||||
? opt.mobileFetch
|
||||
? listEmployee.value.length +
|
||||
(employeeStats.value === listEmployee.value.length ? 1 : 0)
|
||||
: opt.pageSize || pageSize.value
|
||||
: pageSize.value,
|
||||
status:
|
||||
props.currentStatus === 'All'
|
||||
? undefined
|
||||
: props.currentStatus === 'ACTIVE'
|
||||
? 'ACTIVE'
|
||||
: 'INACTIVE',
|
||||
query: props.inputSearch,
|
||||
passport: true,
|
||||
visa: true,
|
||||
startDate: props.searchDate[0],
|
||||
endDate: props.searchDate[1],
|
||||
});
|
||||
if (resultListEmployee) {
|
||||
maxPageEmployee.value = Math.ceil(
|
||||
resultListEmployee.total / pageSize.value,
|
||||
);
|
||||
$q.screen.xs && !(opt && opt.mobileFetch)
|
||||
? listEmployee.value.push(...resultListEmployee.result)
|
||||
: (listEmployee.value = resultListEmployee.result);
|
||||
}
|
||||
|
||||
if (opt && opt.fetchStats)
|
||||
employeeStats.value = await employeeStore.getStatsEmployee();
|
||||
}
|
||||
|
||||
async function openHistory(id: string) {
|
||||
const res = await employeeStore.getEditHistory(id);
|
||||
employeeHistory.value = res.reverse();
|
||||
employeeHistoryDialog.value = true;
|
||||
}
|
||||
|
||||
async function editEmployeeFormPersonal(id: string) {
|
||||
await employeeFormStore.assignFormDataEmployee(id);
|
||||
await props.fetchImageList(
|
||||
id,
|
||||
currentFromDataEmployee.value.selectedImage || '',
|
||||
'employee',
|
||||
);
|
||||
|
||||
employeeFormState.value.isEmployeeEdit = true;
|
||||
employeeFormState.value.dialogType = 'edit';
|
||||
employeeFormState.value.drawerModal = true;
|
||||
}
|
||||
|
||||
async function openSpecificEmployee(id: string) {
|
||||
await employeeFormStore.assignFormDataEmployee(id);
|
||||
await props.fetchImageList(
|
||||
id,
|
||||
currentFromDataEmployee.value.selectedImage || '',
|
||||
'employee',
|
||||
);
|
||||
employeeFormState.value.dialogType = 'info';
|
||||
employeeFormState.value.drawerModal = true;
|
||||
}
|
||||
|
||||
async function toggleStatusEmployee(
|
||||
id: string,
|
||||
status: boolean,
|
||||
employeeName: string,
|
||||
) {
|
||||
const res = await employeeStore.editById(id, {
|
||||
status: !status ? 'ACTIVE' : 'INACTIVE',
|
||||
firstNameEN: employeeName,
|
||||
});
|
||||
if (res && employeeFormState.value.drawerModal) {
|
||||
currentFromDataEmployee.value.status = res.status;
|
||||
}
|
||||
|
||||
await employeeFormStore.assignFormDataEmployee(id);
|
||||
await fetchListEmployee({ mobileFetch: $q.screen.xs });
|
||||
flowStore.rotate();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [
|
||||
props.inputSearch,
|
||||
props.searchDate,
|
||||
props.currentStatus,
|
||||
pageSize.value,
|
||||
],
|
||||
async () => {
|
||||
currentPageEmployee.value = 1;
|
||||
listEmployee.value = [];
|
||||
await fetchListEmployee({ fetchStats: true });
|
||||
|
||||
customerFormState.value.currentCustomerId = undefined;
|
||||
flowStore.rotate();
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => employeeFormState.value.currentCustomerBranch,
|
||||
(e) => {
|
||||
if (!e) return;
|
||||
if (employeeFormState.value.formDataEmployeeSameAddr) {
|
||||
currentFromDataEmployee.value.address = e.address;
|
||||
currentFromDataEmployee.value.addressEN = e.addressEN;
|
||||
currentFromDataEmployee.value.provinceId = e.provinceId;
|
||||
currentFromDataEmployee.value.districtId = e.districtId;
|
||||
currentFromDataEmployee.value.subDistrictId = e.subDistrictId;
|
||||
}
|
||||
currentFromDataEmployee.value.customerBranchId = e.id;
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => employeeFormState.value.formDataEmployeeSameAddr,
|
||||
(isSame) => {
|
||||
if (!employeeFormState.value.currentCustomerBranch) return;
|
||||
if (isSame) {
|
||||
currentFromDataEmployee.value.address =
|
||||
employeeFormState.value.currentCustomerBranch.address;
|
||||
currentFromDataEmployee.value.addressEN =
|
||||
employeeFormState.value.currentCustomerBranch.addressEN;
|
||||
currentFromDataEmployee.value.provinceId =
|
||||
employeeFormState.value.currentCustomerBranch.provinceId;
|
||||
currentFromDataEmployee.value.districtId =
|
||||
employeeFormState.value.currentCustomerBranch.districtId;
|
||||
currentFromDataEmployee.value.subDistrictId =
|
||||
employeeFormState.value.currentCustomerBranch.subDistrictId;
|
||||
}
|
||||
currentFromDataEmployee.value.customerBranchId =
|
||||
employeeFormState.value.currentCustomerBranch.id;
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => currentFromDataEmployee.value.dateOfBirth,
|
||||
(v) => {
|
||||
const isEdit =
|
||||
employeeFormState.value.drawerModal &&
|
||||
employeeFormState.value.isEmployeeEdit;
|
||||
let currentFormDate = v && toISOStringWithTimezone(new Date(v));
|
||||
let currentDate: string = '';
|
||||
|
||||
if (isEdit && employeeFormState.value.currentEmployee) {
|
||||
currentDate = toISOStringWithTimezone(
|
||||
new Date(employeeFormState.value.currentEmployee.dateOfBirth),
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
employeeFormState.value.dialogModal ||
|
||||
(isEdit && currentFormDate !== currentDate)
|
||||
) {
|
||||
const age = calculateAge(
|
||||
currentFromDataEmployee.value.dateOfBirth,
|
||||
'year',
|
||||
);
|
||||
if (currentFromDataEmployee.value.dateOfBirth && Number(age) < 15) {
|
||||
dialog({
|
||||
color: 'warning',
|
||||
icon: 'mdi-alert',
|
||||
title: t('dialog.title.youngWorker15'),
|
||||
cancelText: t('general.edit'),
|
||||
persistent: true,
|
||||
message: t('dialog.message.youngWorker15'),
|
||||
|
||||
cancel: async () => {
|
||||
currentFromDataEmployee.value.dateOfBirth = null;
|
||||
return;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
currentFromDataEmployee.value.dateOfBirth &&
|
||||
Number(age) > 15 &&
|
||||
Number(age) <= 18
|
||||
) {
|
||||
dialog({
|
||||
color: 'warning',
|
||||
icon: 'mdi-alert',
|
||||
title: t('dialog.title.youngWorker18'),
|
||||
cancelText: t('general.cancel'),
|
||||
actionText: t('general.confirm'),
|
||||
persistent: true,
|
||||
message: t('dialog.message.youngWorker18'),
|
||||
action: () => {},
|
||||
cancel: async () => {
|
||||
currentFromDataEmployee.value.dateOfBirth = null;
|
||||
return;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => currentFromDataEmployee.value.image,
|
||||
() => {
|
||||
if (currentFromDataEmployee.value.image !== null)
|
||||
employeeFormState.value.isImageEdit = true;
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
currentPageEmployee.value = 1;
|
||||
listEmployee.value = [];
|
||||
await fetchListEmployee({ fetchStats: true });
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<q-splitter
|
||||
v-model="splitPercent"
|
||||
:limits="[0, 100]"
|
||||
class="col full-width"
|
||||
before-class="overflow-hidden"
|
||||
after-class="overflow-hidden"
|
||||
:disable="$q.screen.lt.sm"
|
||||
>
|
||||
<template v-slot:before>
|
||||
<div
|
||||
class="column q-pa-md surface-1 full-height full-width"
|
||||
style="gap: var(--size-1)"
|
||||
>
|
||||
<q-item
|
||||
active
|
||||
dense
|
||||
active-class="employer-active"
|
||||
class="no-padding items-center rounded full-width"
|
||||
v-close-popup
|
||||
clickable
|
||||
>
|
||||
<span class="q-px-md ellipsis">
|
||||
{{ $t('general.all') }}
|
||||
</span>
|
||||
</q-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:after>
|
||||
<div class="column full-height no-wrap">
|
||||
<!-- employee -->
|
||||
<template
|
||||
v-if="listEmployee && employeeStats > 0 && currentTab === 'employee'"
|
||||
>
|
||||
<div
|
||||
v-if="listEmployee.length === 0"
|
||||
class="row col full-width items-center justify-center"
|
||||
style="min-height: 250px"
|
||||
>
|
||||
<NoData :not-found="!!inputSearch" />
|
||||
</div>
|
||||
|
||||
<article
|
||||
v-if="listEmployee.length !== 0"
|
||||
class="column scroll q-pa-md col"
|
||||
>
|
||||
<q-infinite-scroll
|
||||
:offset="10"
|
||||
@load="
|
||||
(_, done) => {
|
||||
if (
|
||||
$q.screen.gt.xs ||
|
||||
currentPageEmployee === maxPageEmployee
|
||||
)
|
||||
return;
|
||||
currentPageEmployee = currentPageEmployee + 1;
|
||||
fetchListEmployee().then(() =>
|
||||
done(currentPageEmployee >= maxPageEmployee),
|
||||
);
|
||||
}
|
||||
"
|
||||
>
|
||||
<TableEmpoloyee
|
||||
:hide-delete="!canAccess('customer', 'edit')"
|
||||
v-model:page-size="pageSize"
|
||||
v-model:current-page="currentPageEmployee"
|
||||
:grid-view="gridView"
|
||||
:list-employee="listEmployee"
|
||||
:columns-employee="columnsEmployee"
|
||||
:field-selected="fieldSelected"
|
||||
@history="
|
||||
(item: any) => {
|
||||
openHistory(item.id);
|
||||
}
|
||||
"
|
||||
@view="
|
||||
async (item: any) => {
|
||||
employeeFormState.drawerModal = true;
|
||||
employeeFormState.isEmployeeEdit = false;
|
||||
employeeFormStore.assignFormDataEmployee(item.id);
|
||||
await fetchImageList(
|
||||
item.id,
|
||||
item.selectedImage || '',
|
||||
'employee',
|
||||
);
|
||||
}
|
||||
"
|
||||
@edit="(item: any) => editEmployeeFormPersonal(item.id)"
|
||||
@delete="
|
||||
(item: any) => {
|
||||
deleteEmployeeById({
|
||||
id: item.id,
|
||||
fetch: async () =>
|
||||
await fetchListEmployee(
|
||||
currentTab === 'employer'
|
||||
? {
|
||||
page: 1,
|
||||
pageSize: 999,
|
||||
customerId: customerFormState.currentCustomerId,
|
||||
}
|
||||
: {
|
||||
fetchStats: true,
|
||||
mobileFetch: $q.screen.xs,
|
||||
},
|
||||
),
|
||||
});
|
||||
}
|
||||
"
|
||||
@toggle-status="
|
||||
async (item: any) => {
|
||||
triggerChangeStatus(item.id, item.status, item.firstNameEN);
|
||||
}
|
||||
"
|
||||
/>
|
||||
<template v-slot:loading>
|
||||
<div
|
||||
v-if="
|
||||
$q.screen.lt.sm && currentPageEmployee !== maxPageEmployee
|
||||
"
|
||||
class="row justify-center"
|
||||
>
|
||||
<q-spinner-dots color="primary" size="40px" />
|
||||
</div>
|
||||
</template>
|
||||
</q-infinite-scroll>
|
||||
</article>
|
||||
<footer
|
||||
v-if="listEmployee.length !== 0 && $q.screen.gt.xs"
|
||||
class="row justify-between items-center q-px-md q-py-sm"
|
||||
>
|
||||
<div class="row col-4 items-center">
|
||||
<div
|
||||
class="app-text-muted"
|
||||
style="width: 80px"
|
||||
v-if="$q.screen.gt.sm"
|
||||
>
|
||||
{{ $t('general.recordPerPage') }}
|
||||
</div>
|
||||
|
||||
<div><PaginationPageSize v-model="pageSize" /></div>
|
||||
</div>
|
||||
|
||||
<div class="col-4 flex justify-center app-text-muted">
|
||||
{{
|
||||
$q.screen.gt.sm
|
||||
? $t('general.recordsPage', {
|
||||
resultcurrentPage: listEmployee.length,
|
||||
total: employeeStats,
|
||||
})
|
||||
: $t('general.ofPage', {
|
||||
current: listEmployee.length,
|
||||
total: employeeStats,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
|
||||
<div class="col-4 flex justify-end">
|
||||
<PaginationComponent
|
||||
v-model:current-page="currentPageEmployee"
|
||||
v-model:max-page="maxPageEmployee"
|
||||
:fetch-data="
|
||||
async () => {
|
||||
await fetchListEmployee();
|
||||
flowStore.rotate();
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</q-splitter>
|
||||
|
||||
<!-- add employee -->
|
||||
<DialogEmployee
|
||||
:fetch-list-employee="fetchListEmployee"
|
||||
:fetch-image-list="fetchImageList"
|
||||
:current-tab="currentTab"
|
||||
/>
|
||||
|
||||
<!-- ลูกจ้าง edit employee -->
|
||||
<DrawerEmployee
|
||||
:fetch-list-employee="fetchListEmployee"
|
||||
:fetch-image-list="fetchImageList"
|
||||
:current-tab="currentTab"
|
||||
@change-status="
|
||||
(s) =>
|
||||
triggerChangeStatus(
|
||||
currentFromDataEmployee.id,
|
||||
s,
|
||||
currentFromDataEmployee.firstNameEN,
|
||||
)
|
||||
"
|
||||
/>
|
||||
|
||||
<DialogForm
|
||||
:title="$t('general.historyEdit')"
|
||||
hide-footer
|
||||
v-model:modal="employeeHistoryDialog"
|
||||
>
|
||||
<div class="q-pa-md">
|
||||
<HistoryEditComponent
|
||||
v-if="employeeHistory"
|
||||
v-model:history-list="employeeHistory"
|
||||
/>
|
||||
</div>
|
||||
</DialogForm>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.employer-active {
|
||||
background-color: hsla(var(--info-bg) / 0.1);
|
||||
color: hsl(var(--info-bg));
|
||||
}
|
||||
</style>
|
||||
|
|
@ -336,6 +336,7 @@ watch(
|
|||
(v) => (typeof v === 'string' ? (prefixName = v) : '')
|
||||
"
|
||||
@clear="prefixName = ''"
|
||||
:rules="[(val: string) => !!val || $t('form.error.required')]"
|
||||
>
|
||||
<template v-slot:no-option>
|
||||
<q-item>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import { CustomerCreate } from 'stores/customer/types';
|
|||
import EmployerFormAbout from './EmployerFormAbout.vue';
|
||||
import EmployerFormAuthorized from './EmployerFormAuthorized.vue';
|
||||
import { waitAll } from 'src/stores/utils';
|
||||
import FormEmployeePassport from 'src/components/03_customer-management/FormEmployeePassport.vue';
|
||||
import FormEmployeeVisa from 'src/components/03_customer-management/FormEmployeeVisa.vue';
|
||||
import {
|
||||
FormCitizen,
|
||||
CorpFormBusinessRegistration,
|
||||
|
|
|
|||
|
|
@ -10,10 +10,25 @@ import { useI18n } from 'vue-i18n';
|
|||
import ThaiBahtText from 'thai-baht-text';
|
||||
import SelectBusinessType from 'src/components/shared/select/SelectBusinessType.vue';
|
||||
|
||||
import BusinessTypeDialog from 'src/pages/16_business-type-management/BusinessTypeDialog.vue';
|
||||
|
||||
import useBusinessTypeStore from 'src/stores/business-type';
|
||||
|
||||
const { locale } = useI18n({ useScope: 'global' });
|
||||
|
||||
const rawOption = ref();
|
||||
|
||||
const businessTypeDialog = ref<boolean>(false);
|
||||
const formDataBusinessType = ref<{
|
||||
name: string;
|
||||
nameEN: string;
|
||||
}>({
|
||||
name: '',
|
||||
nameEN: '',
|
||||
});
|
||||
|
||||
const useBusinessType = useBusinessTypeStore();
|
||||
|
||||
const businessTypeId = defineModel<string>('businessTypeId');
|
||||
const jobPosition = defineModel<string>('jobPosition');
|
||||
const jobDescription = defineModel<string>('jobDescription');
|
||||
|
|
@ -29,6 +44,8 @@ const typeBusinessENOption = ref([]);
|
|||
const jobPositionOption = ref([]);
|
||||
const jobPositionENOption = ref([]);
|
||||
|
||||
const keySelect = ref<number>(0);
|
||||
|
||||
defineProps<{
|
||||
title?: string;
|
||||
dense?: boolean;
|
||||
|
|
@ -38,6 +55,20 @@ defineProps<{
|
|||
showTitle?: boolean;
|
||||
}>();
|
||||
|
||||
function resetFormBusinessType() {
|
||||
businessTypeDialog.value = false;
|
||||
formDataBusinessType.value = { name: '', nameEN: '' };
|
||||
}
|
||||
|
||||
async function submitBusinessType() {
|
||||
const res = await useBusinessType.create(formDataBusinessType.value);
|
||||
if (res) {
|
||||
businessTypeId.value = res.id;
|
||||
resetFormBusinessType();
|
||||
keySelect.value++;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const resultOption = await fetch('/option/option.json');
|
||||
rawOption.value = await resultOption.json();
|
||||
|
|
@ -121,16 +152,24 @@ let jobPositionENFilter = selectFilterOptionRefMod(
|
|||
</div>
|
||||
|
||||
<SelectBusinessType
|
||||
:key="keySelect"
|
||||
class="col-md-6 col-12"
|
||||
v-model:value="businessTypeId"
|
||||
:readonly
|
||||
creatable
|
||||
lang="tha"
|
||||
:rules="[(val: string) => !!val || $t('form.error.required')]"
|
||||
@create="() => (businessTypeDialog = true)"
|
||||
/>
|
||||
<SelectBusinessType
|
||||
:key="keySelect"
|
||||
class="col-md-6 col-12"
|
||||
v-model:value="businessTypeId"
|
||||
lang="eng"
|
||||
:readonly
|
||||
creatable
|
||||
lang="eng"
|
||||
:rules="[(val: string) => !!val || $t('form.error.required')]"
|
||||
@create="() => (businessTypeDialog = true)"
|
||||
/>
|
||||
|
||||
<q-select
|
||||
|
|
@ -282,4 +321,12 @@ let jobPositionENFilter = selectFilterOptionRefMod(
|
|||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<BusinessTypeDialog
|
||||
ref="refBusinessTypeDialog"
|
||||
@close="resetFormBusinessType()"
|
||||
@submit="submitBusinessType()"
|
||||
v-model="businessTypeDialog"
|
||||
v-model:data="formDataBusinessType"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@ const contactName = defineModel<string>('contactName');
|
|||
const email = defineModel<string>('email');
|
||||
const contactTel = defineModel<string>('contactTel');
|
||||
const officeTel = defineModel<string>('officeTel');
|
||||
const agent = defineModel<string>('agent');
|
||||
|
||||
const agentUserId = defineModel<string>('agentUserId');
|
||||
</script>
|
||||
|
||||
|
|
@ -109,7 +107,6 @@ const agentUserId = defineModel<string>('agentUserId');
|
|||
/>
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<SelectAgent
|
||||
:label="$t('customer.form.agent')"
|
||||
v-model:value="agentUserId"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ import { useRoute } from 'vue-router';
|
|||
|
||||
export const useCustomerForm = defineStore('form-customer', () => {
|
||||
const customerStore = useCustomerStore();
|
||||
const onCreateImageList = ref<{
|
||||
selectedImage: string;
|
||||
list: { url: string; imgFile: File | null; name: string }[];
|
||||
}>({ selectedImage: '', list: [] });
|
||||
|
||||
const { t } = useI18n();
|
||||
const flowStore = useFlowStore();
|
||||
|
|
@ -30,6 +34,8 @@ export const useCustomerForm = defineStore('form-customer', () => {
|
|||
|
||||
const registerAbleBranchOption = ref<{ id: string; name: string }[]>();
|
||||
|
||||
const currentBranchRootId = ref<string>('');
|
||||
|
||||
const tabFieldRequired = ref<{
|
||||
[key: string]: (keyof CustomerBranchCreate)[];
|
||||
}>({
|
||||
|
|
@ -82,6 +88,7 @@ export const useCustomerForm = defineStore('form-customer', () => {
|
|||
formDataOcr: Record<string, any>;
|
||||
isImageEdit: boolean;
|
||||
currentCustomerId?: string;
|
||||
imageList: { list: string[]; selectedImage: string };
|
||||
}>({
|
||||
dialogType: 'info',
|
||||
dialogOpen: false,
|
||||
|
|
@ -98,6 +105,7 @@ export const useCustomerForm = defineStore('form-customer', () => {
|
|||
treeFile: [],
|
||||
formDataOcr: {},
|
||||
isImageEdit: false,
|
||||
imageList: { list: [], selectedImage: '' },
|
||||
});
|
||||
|
||||
watch(
|
||||
|
|
@ -160,6 +168,7 @@ export const useCustomerForm = defineStore('form-customer', () => {
|
|||
state.value.editCustomerCode = data.code;
|
||||
state.value.customerImageUrl = `${baseUrl}/customer/${id}/image/${data.selectedImage}`;
|
||||
state.value.defaultCustomerImageUrl = `${baseUrl}/customer/${id}/image/${data.selectedImage}`;
|
||||
currentBranchRootId.value = data.branch[0].id || '';
|
||||
|
||||
resetFormData.registeredBranchId = data.registeredBranchId;
|
||||
resetFormData.status = data.status;
|
||||
|
|
@ -255,7 +264,6 @@ export const useCustomerForm = defineStore('form-customer', () => {
|
|||
async function addCurrentCustomerBranch() {
|
||||
if (currentFormData.value.customerBranch?.some((v) => !v.id)) return;
|
||||
currentFormData.value.customerBranch?.push({
|
||||
id: '',
|
||||
customerId: '',
|
||||
branchCode:
|
||||
currentFormData.value.customerBranch.length !== 0
|
||||
|
|
@ -495,11 +503,14 @@ export const useCustomerForm = defineStore('form-customer', () => {
|
|||
}
|
||||
|
||||
return {
|
||||
onCreateImageList,
|
||||
tabFieldRequired,
|
||||
registerAbleBranchOption,
|
||||
state,
|
||||
resetFormData,
|
||||
currentFormData,
|
||||
currentBranchRootId,
|
||||
|
||||
isFormDataDifferent,
|
||||
resetForm,
|
||||
assignFormData,
|
||||
|
|
@ -779,6 +790,7 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
|||
}
|
||||
| undefined;
|
||||
ocr: boolean;
|
||||
imageList: { list: string[]; selectedImage: string };
|
||||
}>({
|
||||
currentBranchId: '',
|
||||
isImageEdit: false,
|
||||
|
|
@ -803,6 +815,7 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
|||
infoEmployeePersonCard: [],
|
||||
formDataEmployeeOwner: undefined,
|
||||
ocr: false,
|
||||
imageList: { list: [], selectedImage: '' },
|
||||
});
|
||||
|
||||
const defaultFormData: EmployeeCreate & { image?: File } = {
|
||||
|
|
@ -955,6 +968,7 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
|||
state.value.currentIndexVisa = -1;
|
||||
state.value.currentIndexCheckup = -1;
|
||||
state.value.currentIndexWorkHistory = -1;
|
||||
state.value.imageList = { list: [], selectedImage: '' };
|
||||
// state.value.currentTab = 'personalInfo';
|
||||
if (clean) {
|
||||
state.value.formDataEmployeeOwner = undefined;
|
||||
|
|
@ -1384,12 +1398,10 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
|||
statusSave: true,
|
||||
})),
|
||||
),
|
||||
employeeOtherInfo: structuredClone(
|
||||
{
|
||||
...payload.employeeOtherInfo,
|
||||
statusSave: !!payload.employeeOtherInfo?.id ? true : false,
|
||||
} || {},
|
||||
),
|
||||
employeeOtherInfo: structuredClone({
|
||||
...(payload.employeeOtherInfo ?? {}),
|
||||
statusSave: true,
|
||||
}),
|
||||
employeeWork: structuredClone(
|
||||
payload.employeeWork?.length === 0
|
||||
? state.value.dialogModal
|
||||
|
|
@ -1663,6 +1675,7 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
|||
state,
|
||||
currentFromDataEmployee,
|
||||
resetEmployeeData,
|
||||
|
||||
addPassport,
|
||||
addVisa,
|
||||
addCheckup,
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ const flowStore = useFlowStore();
|
|||
const userBranch = useMyBranch();
|
||||
const navigatorStore = useNavigator();
|
||||
const customerStore = useCustomerStore();
|
||||
|
||||
const {
|
||||
fetchListOfOptionBranch,
|
||||
customerFormUndo,
|
||||
|
|
@ -76,6 +75,7 @@ const {
|
|||
const {
|
||||
state: customerFormState,
|
||||
currentFormData: customerFormData,
|
||||
currentBranchRootId,
|
||||
registerAbleBranchOption,
|
||||
tabFieldRequired,
|
||||
} = storeToRefs(customerFormStore);
|
||||
|
|
@ -89,6 +89,8 @@ const fieldSelectedOption = computed(() => {
|
|||
value: v.name,
|
||||
}));
|
||||
});
|
||||
|
||||
const keyAddDialog = ref<number>(0);
|
||||
const special = ref(false);
|
||||
const branchId = ref('');
|
||||
const agentPrice = ref<boolean>(false);
|
||||
|
|
@ -865,6 +867,7 @@ async function filterBySellerId() {
|
|||
<!-- NOTE: START - Quotation Form, Add Quotation -->
|
||||
|
||||
<DialogForm
|
||||
:key="keyAddDialog"
|
||||
:title="$t('general.add', { text: $t('quotation.title') })"
|
||||
v-model:modal="pageState.addModal"
|
||||
:submit-label="$t('general.add', { text: $t('quotation.title') })"
|
||||
|
|
@ -874,13 +877,14 @@ async function filterBySellerId() {
|
|||
:submit="
|
||||
() => {
|
||||
quotationFormState.mode = 'create';
|
||||
quotationFormData.customerBranchId = currentBranchRootId;
|
||||
triggerQuotationDialog({ statusDialog: 'create' });
|
||||
}
|
||||
"
|
||||
:close="
|
||||
() => {
|
||||
branchId = '';
|
||||
quotationFormData.customerBranchId = '';
|
||||
currentBranchRootId = '';
|
||||
}
|
||||
"
|
||||
:beforeClose="
|
||||
|
|
@ -930,7 +934,7 @@ async function filterBySellerId() {
|
|||
v-model:agent-price="agentPrice"
|
||||
v-model:branch-id="branchId"
|
||||
v-model:special="special"
|
||||
v-model:customer-branch-id="quotationFormData.customerBranchId"
|
||||
v-model:customer-branch-id="currentBranchRootId"
|
||||
@add-customer="triggerSelectTypeCustomerd()"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -999,6 +1003,7 @@ async function filterBySellerId() {
|
|||
() => {
|
||||
customerFormState.dialogModal = false;
|
||||
onCreateImageList = { selectedImage: '', list: [] };
|
||||
keyAddDialog++;
|
||||
}
|
||||
"
|
||||
>
|
||||
|
|
@ -1190,7 +1195,7 @@ async function filterBySellerId() {
|
|||
customerFormData.customerBranch[0].legalPersonNo
|
||||
"
|
||||
v-model:business-type="
|
||||
customerFormData.customerBranch[0].businessType
|
||||
customerFormData.customerBranch[0].businessTypeId
|
||||
"
|
||||
v-model:job-position="
|
||||
customerFormData.customerBranch[0].jobPosition
|
||||
|
|
@ -1271,7 +1276,6 @@ async function filterBySellerId() {
|
|||
res = await customerStore.createBranch({
|
||||
...customerFormData.customerBranch[idx],
|
||||
customerId: customerFormState.editCustomerId || '',
|
||||
id: undefined,
|
||||
});
|
||||
}
|
||||
if (res) {
|
||||
|
|
|
|||
|
|
@ -80,6 +80,8 @@ import { RouterLink, useRoute } from 'vue-router';
|
|||
import { initLang, initTheme, Lang } from 'src/utils/ui';
|
||||
import { convertTemplate } from 'src/utils/string-template';
|
||||
|
||||
import { CustomerBranch } from 'src/stores/customer';
|
||||
|
||||
type Node = {
|
||||
[key: string]: any;
|
||||
opened?: boolean;
|
||||
|
|
@ -111,6 +113,8 @@ const $q = useQuasar();
|
|||
const openQuotation = ref<boolean>(false);
|
||||
const formMetadata = ref();
|
||||
|
||||
const customerBranchOption = ref<CustomerBranch>();
|
||||
|
||||
const rowsRequestList = ref<RequestData[]>([]);
|
||||
|
||||
const {
|
||||
|
|
@ -244,32 +248,25 @@ function getPrice(
|
|||
return a;
|
||||
}
|
||||
|
||||
const originalPrice = c.pricePerUnit;
|
||||
const finalPriceWithVat = precisionRound(
|
||||
originalPrice * (1 + (config.value?.vat || 0.07)),
|
||||
);
|
||||
const finalPriceNoVat =
|
||||
finalPriceWithVat / (1 + (config.value?.vat || 0.07));
|
||||
|
||||
const price = finalPriceNoVat * c.amount;
|
||||
const vat =
|
||||
(finalPriceNoVat * c.amount - c.discount) * (config.value?.vat || 0.07);
|
||||
|
||||
const calcVat =
|
||||
c.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat'];
|
||||
const vatFactor = calcVat ? (config.value?.vat ?? 0.07) : 0;
|
||||
|
||||
a.totalPrice = precisionRound(a.totalPrice + price);
|
||||
a.totalDiscount = precisionRound(a.totalDiscount + Number(c.discount));
|
||||
a.vat = calcVat ? precisionRound(a.vat + vat) : a.vat;
|
||||
const pricePerUnit =
|
||||
precisionRound(c.pricePerUnit * (1 + vatFactor)) / (1 + vatFactor);
|
||||
|
||||
const price =
|
||||
(pricePerUnit * c.amount * (1 + vatFactor) - c.discount) /
|
||||
(1 + vatFactor);
|
||||
const vat = price * vatFactor;
|
||||
|
||||
a.totalPrice = precisionRound(a.totalPrice + price + c.discount);
|
||||
a.totalDiscount = precisionRound(a.totalDiscount + c.discount);
|
||||
a.vat = precisionRound(a.vat + vat);
|
||||
a.vatExcluded = calcVat
|
||||
? a.vatExcluded
|
||||
: precisionRound(a.vatExcluded + price);
|
||||
a.finalPrice = precisionRound(
|
||||
a.totalPrice -
|
||||
a.totalDiscount +
|
||||
a.vat -
|
||||
Number(quotationFormData.value.discount || 0),
|
||||
);
|
||||
a.finalPrice = precisionRound(a.totalPrice - a.totalDiscount + a.vat);
|
||||
|
||||
return a;
|
||||
},
|
||||
|
|
@ -719,19 +716,13 @@ function handleUpdateProductTable(
|
|||
// handleChangePayType(quotationFormData.value.payCondition);
|
||||
// calc price
|
||||
const calc = (c: QuotationPayload['productServiceList'][number]) => {
|
||||
const originalPrice = c.pricePerUnit || 0;
|
||||
const finalPriceWithVat = precisionRound(
|
||||
originalPrice * (1 + (config.value?.vat || 0.07)),
|
||||
);
|
||||
const finalPriceNoVat =
|
||||
finalPriceWithVat / (1 + (config.value?.vat || 0.07));
|
||||
|
||||
const price = finalPriceNoVat * c.amount;
|
||||
const vat = c.product.calcVat
|
||||
? (finalPriceNoVat * c.amount - (c.discount || 0)) *
|
||||
(config.value?.vat || 0.07)
|
||||
: 0;
|
||||
return precisionRound(price + vat);
|
||||
const calcVat =
|
||||
c.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat'];
|
||||
const vatFactor = calcVat ? (config.value?.vat ?? 0.07) : 0;
|
||||
const pricePerUnit =
|
||||
precisionRound(c.pricePerUnit * (1 + vatFactor)) / (1 + vatFactor);
|
||||
const price = pricePerUnit * c.amount * (1 + vatFactor) - c.discount;
|
||||
return precisionRound(price);
|
||||
};
|
||||
|
||||
// installment
|
||||
|
|
@ -784,21 +775,16 @@ function toggleDeleteProduct(index: number) {
|
|||
|
||||
// cal curr amount
|
||||
if (currPaySplit && currTempPaySplit) {
|
||||
const price = agentPrice.value
|
||||
? currProduct.product.agentPrice
|
||||
: currProduct.product.price;
|
||||
const pricePerUnit = currProduct.product.vatIncluded
|
||||
? price / (1 + (config.value?.vat || 0.07))
|
||||
: price;
|
||||
const vat =
|
||||
(pricePerUnit * currProduct.amount - currProduct.discount) *
|
||||
(config.value?.vat || 0.07);
|
||||
const finalPrice =
|
||||
pricePerUnit * currProduct.amount +
|
||||
vat -
|
||||
Number(currProduct.discount || 0);
|
||||
const calcVat =
|
||||
currProduct.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat'];
|
||||
const vatFactor = calcVat ? (config.value?.vat ?? 0.07) : 0;
|
||||
|
||||
currTempPaySplit.amount = currPaySplit.amount - finalPrice;
|
||||
const price = precisionRound(
|
||||
currProduct.pricePerUnit * currProduct.amount * (1 + vatFactor) -
|
||||
currProduct.discount,
|
||||
);
|
||||
|
||||
currTempPaySplit.amount = currPaySplit.amount - price;
|
||||
currPaySplit.amount = currTempPaySplit.amount;
|
||||
}
|
||||
|
||||
|
|
@ -853,8 +839,7 @@ function convertToTable(nodes: Node[]) {
|
|||
tempTableProduct.value = JSON.parse(JSON.stringify(list));
|
||||
|
||||
if (nodes.length > 0) {
|
||||
quotationFormData.value.paySplit = Array.apply(
|
||||
null,
|
||||
quotationFormData.value.paySplit = Array.from(
|
||||
new Array(quotationFormData.value.paySplitCount),
|
||||
).map((_, i) => ({
|
||||
no: i + 1,
|
||||
|
|
@ -1074,6 +1059,13 @@ watch(
|
|||
},
|
||||
);
|
||||
|
||||
watch(customerBranchOption, () => {
|
||||
if (!customerBranchOption.value) return;
|
||||
|
||||
quotationFormData.value.contactName = customerBranchOption.value.contactName;
|
||||
quotationFormData.value.contactTel = customerBranchOption.value.contactTel;
|
||||
});
|
||||
|
||||
// async function searchEmployee(text: string) {
|
||||
// let query: string | undefined = text;
|
||||
// let pageSize = 50;
|
||||
|
|
@ -1519,6 +1511,7 @@ function covertToNode() {
|
|||
v-model:customer-branch-id="
|
||||
quotationFormData.customerBranchId
|
||||
"
|
||||
v-model:customer-branch-option="customerBranchOption"
|
||||
:readonly="readonly"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -1856,7 +1849,7 @@ function covertToNode() {
|
|||
name: selectedWorker.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(),
|
||||
`${v.employeePassport.length !== 0 ? v.employeePassport[0].number + '_' : ''}${v.namePrefix}.${v.firstNameEN ? `${v.firstNameEN} ${v.lastNameEN}` : `${v.firstName} ${v.lastName}`} `.toUpperCase(),
|
||||
),
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -446,7 +446,9 @@ watch(
|
|||
<span class="q-ml-auto">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
summaryPrice.totalPrice - summaryPrice.totalDiscount,
|
||||
summaryPrice.totalPrice -
|
||||
summaryPrice.totalDiscount -
|
||||
summaryPrice.vatExcluded,
|
||||
2,
|
||||
)
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts" setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onMounted, nextTick, ref, watch } from 'vue';
|
||||
import { onMounted, nextTick, ref, watch, toRaw } from 'vue';
|
||||
import { precisionRound } from 'src/utils/arithmetic';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import ThaiBahtText from 'thai-baht-text';
|
||||
|
|
@ -175,6 +175,8 @@ enum View {
|
|||
const view = ref<View>(View.Quotation);
|
||||
|
||||
onMounted(async () => {
|
||||
await configStore.getConfig();
|
||||
|
||||
const currentDocumentType = new URL(window.location.href).searchParams.get(
|
||||
'type',
|
||||
);
|
||||
|
|
@ -259,18 +261,6 @@ onMounted(async () => {
|
|||
productList.value =
|
||||
(data?.value?.productServiceList ?? data.value?.productServiceList).map(
|
||||
(v) => {
|
||||
const originalPrice = v.pricePerUnit;
|
||||
const finalPriceWithVat = precisionRound(
|
||||
originalPrice * (1 + (config.value?.vat || 0.07)),
|
||||
);
|
||||
const finalPriceNoVat =
|
||||
finalPriceWithVat / (1 + (config.value?.vat || 0.07));
|
||||
|
||||
const price = finalPriceNoVat * v.amount - v.discount;
|
||||
const vat =
|
||||
(finalPriceNoVat * v.amount - v.discount) *
|
||||
(config.value?.vat || 0.07);
|
||||
|
||||
return {
|
||||
id: v.product.id,
|
||||
code: v.product.code,
|
||||
|
|
@ -279,8 +269,8 @@ onMounted(async () => {
|
|||
pricePerUnit: v.pricePerUnit || 0,
|
||||
discount: v.discount || 0,
|
||||
vat: v.vat || 0,
|
||||
value: precisionRound(price + (v.product.calcVat ? vat : 0)),
|
||||
calcVat: v.product.calcVat,
|
||||
value: 0,
|
||||
calcVat: v.vat > 0,
|
||||
product: v.product,
|
||||
};
|
||||
},
|
||||
|
|
@ -292,23 +282,17 @@ onMounted(async () => {
|
|||
[]
|
||||
).reduce(
|
||||
(a, c) => {
|
||||
const originalPrice = c.pricePerUnit;
|
||||
const finalPriceWithVat = precisionRound(
|
||||
originalPrice * (1 + (config.value?.vat || 0.07)),
|
||||
);
|
||||
const finalPriceNoVat =
|
||||
finalPriceWithVat / (1 + (config.value?.vat || 0.07));
|
||||
const calcVat = c.vat > 0;
|
||||
const vatFactor = calcVat ? (config.value?.vat ?? 0.07) : 0;
|
||||
const pricePerUnit =
|
||||
precisionRound(c.pricePerUnit * (1 + vatFactor)) / (1 + vatFactor);
|
||||
const price =
|
||||
(pricePerUnit * c.amount * (1 + vatFactor) - c.discount) /
|
||||
(1 + vatFactor);
|
||||
|
||||
const price = finalPriceNoVat * c.amount;
|
||||
const vat =
|
||||
(finalPriceNoVat * c.amount - c.discount) * (config.value?.vat || 0.07);
|
||||
|
||||
const calcVat =
|
||||
c.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat'];
|
||||
|
||||
a.totalPrice = precisionRound(a.totalPrice + price);
|
||||
a.totalPrice = precisionRound(a.totalPrice + price + c.discount);
|
||||
a.totalDiscount = precisionRound(a.totalDiscount + Number(c.discount));
|
||||
a.vat = calcVat ? precisionRound(a.vat + vat) : a.vat;
|
||||
a.vat = calcVat ? precisionRound(a.vat + c.vat) : a.vat;
|
||||
a.vatExcluded = calcVat
|
||||
? a.vatExcluded
|
||||
: precisionRound(a.vatExcluded + price);
|
||||
|
|
@ -334,16 +318,12 @@ onMounted(async () => {
|
|||
|
||||
function calcPrice(c: Product) {
|
||||
const originalPrice = c.pricePerUnit;
|
||||
const finalPriceWithVat = precisionRound(
|
||||
originalPrice + originalPrice * (config.value?.vat || 0.07),
|
||||
const finalPricePerUnit = precisionRound(
|
||||
originalPrice +
|
||||
(c.calcVat ? originalPrice * (config.value?.vat || 0.07) : 0),
|
||||
);
|
||||
const finalPriceNoVat = finalPriceWithVat / (1 + (config.value?.vat || 0.07));
|
||||
|
||||
const price = finalPriceNoVat * c.amount - c.discount;
|
||||
const vat = c.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat']
|
||||
? (finalPriceNoVat * c.amount - c.discount) * (config.value?.vat || 0.07)
|
||||
: 0;
|
||||
return precisionRound(price + vat);
|
||||
const price = finalPricePerUnit * c.amount - c.discount;
|
||||
return precisionRound(price);
|
||||
}
|
||||
|
||||
async function closeTab() {
|
||||
|
|
@ -427,31 +407,15 @@ function print() {
|
|||
<td>{{ v.detail }}</td>
|
||||
<td style="text-align: right">{{ v.amount }}</td>
|
||||
<td style="text-align: right">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
v.pricePerUnit +
|
||||
(v.product[agentPrice ? 'agentPriceCalcVat' : 'calcVat']
|
||||
? v.pricePerUnit * (config?.vat || 0.07)
|
||||
: 0),
|
||||
2,
|
||||
)
|
||||
}}
|
||||
{{ formatNumberDecimal(v.pricePerUnit, 2) }}
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
{{ formatNumberDecimal(v.discount, 2) }}
|
||||
<template v-if="v.discount !== 0">
|
||||
{{ formatNumberDecimal(v.discount, 2) }} ฿
|
||||
</template>
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
v.product[agentPrice ? 'agentPriceCalcVat' : 'calcVat']
|
||||
? precisionRound(
|
||||
(v.pricePerUnit * v.amount - v.discount) *
|
||||
(config?.vat || 0.07),
|
||||
)
|
||||
: 0,
|
||||
2,
|
||||
)
|
||||
}}
|
||||
{{ Math.round((v.vat > 0 ? config?.vat || 0.07 : 0) * 100) }}%
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
{{ formatNumberDecimal(calcPrice(v), 2) }}
|
||||
|
|
@ -511,7 +475,9 @@ function print() {
|
|||
<td class="text-right">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
summaryPrice.totalPrice - summaryPrice.totalDiscount,
|
||||
summaryPrice.totalPrice -
|
||||
summaryPrice.totalDiscount -
|
||||
summaryPrice.vatExcluded,
|
||||
2,
|
||||
)
|
||||
}}
|
||||
|
|
@ -600,7 +566,7 @@ function print() {
|
|||
details?.worker.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(),
|
||||
`${v.employeePassport.length !== 0 ? v.employeePassport[0].number + '_' : ''}${v.namePrefix}. ${v.firstNameEN ? `${v.firstNameEN} ${v.lastNameEN}` : `${v.firstName} ${v.lastName}`} `.toUpperCase(),
|
||||
) || [],
|
||||
},
|
||||
}) || '-'
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@ function titleMode(mode: View): string {
|
|||
})
|
||||
}}
|
||||
</span>
|
||||
<span>เลขประจำตัวผู้เสียภาษี {{ branch.taxNo }}</span>
|
||||
<span>เบอร์โทร {{ branch.telephoneNo }}</span>
|
||||
<span>{{ $t('branch.form.taxNo') }} {{ branch.taxNo }}</span>
|
||||
<span>{{ $t('taskOrder.telephone') }} {{ branch.telephoneNo }}</span>
|
||||
<span>{{ branch.webUrl }}</span>
|
||||
</article>
|
||||
<article>
|
||||
|
|
@ -105,8 +105,18 @@ function titleMode(mode: View): string {
|
|||
})
|
||||
}}
|
||||
</span>
|
||||
<span>เลขประจำตัวผู้เสียภาษี {{ customer.citizenId }}</span>
|
||||
<span>เบอร์โทร {{ customer.telephoneNo }}</span>
|
||||
<span>
|
||||
{{
|
||||
customer.customer.customerType === 'CORP'
|
||||
? `${$t('customer.form.legalPersonNo')} `
|
||||
: `${$t('customer.form.taxpayyerNo')} `
|
||||
}}{{
|
||||
customer.customer.customerType === 'CORP'
|
||||
? customer.codeCustomer
|
||||
: customer.citizenId
|
||||
}}
|
||||
</span>
|
||||
<span>{{ $t('taskOrder.telephone') }} {{ customer.telephoneNo }}</span>
|
||||
</article>
|
||||
</section>
|
||||
<section class="detail-quotation-info">
|
||||
|
|
|
|||
|
|
@ -202,40 +202,44 @@ onMounted(async () => {
|
|||
})
|
||||
.reduce(
|
||||
(a, c) => {
|
||||
const priceNoVat = c.product.serviceChargeVatIncluded
|
||||
? c.pricePerUnit / (1 + (config.value?.vat || 0.07))
|
||||
: c.pricePerUnit;
|
||||
const adjustedPriceWithVat = precisionRound(
|
||||
priceNoVat * (1 + (config.value?.vat || 0.07)),
|
||||
);
|
||||
const adjustedPriceNoVat =
|
||||
adjustedPriceWithVat / (1 + (config.value?.vat || 0.07));
|
||||
const priceDiscountNoVat = adjustedPriceNoVat * c.amount - c.discount;
|
||||
const vatFactor = c.product.serviceChargeCalcVat
|
||||
? (config.value?.vat ?? 0.07)
|
||||
: 0;
|
||||
|
||||
const rawVatTotal = priceDiscountNoVat * (config.value?.vat || 0.07);
|
||||
const rawVat = rawVatTotal / c.amount;
|
||||
const pricePerUnit =
|
||||
precisionRound(c.product.serviceCharge * (1 + vatFactor)) /
|
||||
(1 + vatFactor);
|
||||
|
||||
const amount = c.amount;
|
||||
const discount = c.discount || 0;
|
||||
|
||||
const price =
|
||||
(pricePerUnit * amount * (1 + vatFactor) - discount) /
|
||||
(1 + vatFactor);
|
||||
|
||||
const vat = price * vatFactor;
|
||||
|
||||
product.value.push({
|
||||
id: c.product.id,
|
||||
code: c.product.code,
|
||||
detail: c.product.name,
|
||||
priceUnit: precisionRound(priceNoVat),
|
||||
priceUnit: precisionRound(pricePerUnit),
|
||||
amount: c.amount,
|
||||
discount: c.discount,
|
||||
vat: c.product.serviceChargeCalcVat ? precisionRound(rawVat) : 0,
|
||||
vat: c.product.serviceChargeCalcVat ? precisionRound(vat) : 0,
|
||||
value: precisionRound(
|
||||
priceNoVat * c.amount +
|
||||
(c.product.serviceChargeCalcVat ? rawVatTotal : 0),
|
||||
pricePerUnit * c.amount +
|
||||
(c.product.serviceChargeCalcVat ? vat : 0),
|
||||
),
|
||||
});
|
||||
|
||||
a.totalPrice = a.totalPrice + priceDiscountNoVat;
|
||||
a.totalDiscount = a.totalDiscount + Number(c.discount);
|
||||
a.vat = c.product.calcVat ? a.vat + rawVatTotal : a.vat;
|
||||
a.vatExcluded = c.product.calcVat
|
||||
a.totalPrice = precisionRound(a.totalPrice + price + discount);
|
||||
a.totalDiscount = precisionRound(a.totalDiscount + discount);
|
||||
a.vat = precisionRound(a.vat + vat);
|
||||
a.vatExcluded = c.product.serviceChargeCalcVat
|
||||
? a.vatExcluded
|
||||
: precisionRound(a.vatExcluded + priceDiscountNoVat);
|
||||
a.finalPrice = a.totalPrice - a.totalDiscount + a.vat;
|
||||
: precisionRound(a.vatExcluded + price);
|
||||
a.finalPrice = precisionRound(a.totalPrice - a.totalDiscount + a.vat);
|
||||
return a;
|
||||
},
|
||||
{
|
||||
|
|
@ -311,11 +315,7 @@ function closeAble() {
|
|||
border-bottom: 2px solid var(--main);
|
||||
"
|
||||
>
|
||||
{{
|
||||
viewType === 'docReceive'
|
||||
? $t('taskOrder.goodReceipt')
|
||||
: $t('preview.taskOrder')
|
||||
}}
|
||||
{{ $t('preview.productList') }}
|
||||
</span>
|
||||
|
||||
<table ref="elements" class="q-mb-sm" cellpadding="0" style="width: 100%">
|
||||
|
|
@ -339,7 +339,9 @@ function closeAble() {
|
|||
{{ formatNumberDecimal(v.priceUnit, 2) }}
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
{{ formatNumberDecimal(v.discount, 2) }}
|
||||
<template v-if="v.discount !== 0">
|
||||
{{ formatNumberDecimal(v.discount, 2) }} ฿
|
||||
</template>
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
{{ formatNumberDecimal(v.vat, 2) }}
|
||||
|
|
@ -401,7 +403,9 @@ function closeAble() {
|
|||
<td class="text-right">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
summaryPrice.totalPrice - summaryPrice.totalDiscount,
|
||||
summaryPrice.totalPrice -
|
||||
summaryPrice.totalDiscount -
|
||||
summaryPrice.vatExcluded,
|
||||
2,
|
||||
)
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ defineProps<{
|
|||
})
|
||||
}}
|
||||
</span>
|
||||
<span>เลขประจำตัวผู้เสียภาษี {{ branch.taxNo }}</span>
|
||||
<span>เบอร์โทร {{ branch.telephoneNo }}</span>
|
||||
<span>{{ $t('branch.form.taxNo') }} {{ branch.taxNo }}</span>
|
||||
<span>{{ $t('taskOrder.telephone') }} {{ branch.telephoneNo }}</span>
|
||||
<span>{{ branch.webUrl }}</span>
|
||||
</article>
|
||||
<article>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ import { storeToRefs } from 'pinia';
|
|||
import BadgeComponent from 'src/components/BadgeComponent.vue';
|
||||
import { TaskStatus } from 'src/stores/task-order/types';
|
||||
|
||||
import { useTaskOrderForm } from '../form';
|
||||
|
||||
const taskOrderFormStore = useTaskOrderForm();
|
||||
|
||||
const currentBtnOpen = ref<boolean[]>([]);
|
||||
const configStore = useConfigStore();
|
||||
const { data: config } = storeToRefs(configStore);
|
||||
|
|
@ -61,60 +65,28 @@ function openList(index: number) {
|
|||
}
|
||||
|
||||
function calcPricePerUnit(product: RequestWork['productService']['product']) {
|
||||
const val = props.creditNote
|
||||
? props.agentPrice
|
||||
? product.agentPrice
|
||||
: product.price
|
||||
: product.serviceCharge;
|
||||
|
||||
if (
|
||||
product[
|
||||
props.creditNote
|
||||
? props.agentPrice
|
||||
? 'agentPriceCalcVat'
|
||||
: 'calcVat'
|
||||
: 'serviceChargeCalcVat'
|
||||
]
|
||||
) {
|
||||
return val / (1 + (config.value?.vat || 0.07));
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
return product.serviceCharge;
|
||||
}
|
||||
|
||||
function calcPrice(
|
||||
product: RequestWork['productService']['product'],
|
||||
amount: number,
|
||||
) {
|
||||
const pricePerUnit = props.creditNote
|
||||
? props.agentPrice
|
||||
? product.agentPrice
|
||||
: product.price
|
||||
: product.serviceCharge;
|
||||
const discount =
|
||||
taskProduct.value.find((v) => v.productId === product.id)?.discount || 0;
|
||||
const priceNoVat = product[
|
||||
props.creditNote
|
||||
? props.agentPrice
|
||||
? 'agentPriceVatIncluded'
|
||||
: 'vatIncluded'
|
||||
: 'serviceChargeVatIncluded'
|
||||
]
|
||||
? pricePerUnit / (1 + (config.value?.vat || 0.07))
|
||||
: pricePerUnit;
|
||||
const priceDiscountNoVat = priceNoVat * amount - discount;
|
||||
|
||||
const rawVatTotal = product[
|
||||
props.creditNote
|
||||
? props.agentPrice
|
||||
? 'agentPriceCalcVat'
|
||||
: 'calcVat'
|
||||
: 'serviceChargeCalcVat'
|
||||
]
|
||||
? priceDiscountNoVat * (config.value?.vat || 0.07)
|
||||
const vatFactor = product.serviceChargeCalcVat
|
||||
? (config.value?.vat ?? 0.07)
|
||||
: 0;
|
||||
|
||||
return precisionRound(priceNoVat * amount + rawVatTotal);
|
||||
const discount =
|
||||
taskProduct.value.find((v) => v.productId === product.id)?.discount || 0;
|
||||
|
||||
const pricePerUnit = precisionRound(product.serviceCharge);
|
||||
|
||||
const price =
|
||||
(pricePerUnit * amount * (1 + vatFactor) - discount) / (1 + vatFactor);
|
||||
|
||||
const vat = price * vatFactor;
|
||||
|
||||
return price + vat;
|
||||
}
|
||||
|
||||
function taskOrderStatus(value: TaskStatus) {
|
||||
|
|
@ -247,7 +219,7 @@ function taskOrderStatus(value: TaskStatus) {
|
|||
{{
|
||||
formatNumberDecimal(
|
||||
calcPricePerUnit(props.row.product) +
|
||||
(props.row.product.calcVat
|
||||
(props.row.product.serviceChargeCalcVat
|
||||
? calcPricePerUnit(props.row.product) *
|
||||
(config?.vat || 0.07)
|
||||
: 0),
|
||||
|
|
@ -298,11 +270,16 @@ function taskOrderStatus(value: TaskStatus) {
|
|||
<q-td class="text-right" v-if="!creditNote">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
calcPricePerUnit(props.row.product) * props.row.list.length -
|
||||
(taskProduct.find(
|
||||
(v) => v.productId === props.row.product.id,
|
||||
)?.discount || 0),
|
||||
2,
|
||||
props.row.product.serviceChargeCalcVat
|
||||
? (calcPricePerUnit(props.row.product) *
|
||||
props.row.list.length *
|
||||
(1 + (config?.vat || 0.07))) /
|
||||
(1 + (config?.vat || 0.07))
|
||||
: calcPricePerUnit(props.row.product) *
|
||||
props.row.list.length -
|
||||
taskProduct.find(
|
||||
(v) => v.productId === props.row.product.id,
|
||||
)?.discount || 0,
|
||||
)
|
||||
}}
|
||||
</q-td>
|
||||
|
|
@ -310,17 +287,16 @@ function taskOrderStatus(value: TaskStatus) {
|
|||
<q-td class="text-right" v-if="!creditNote">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
props.row.product.calcVat
|
||||
? precisionRound(
|
||||
(calcPricePerUnit(props.row.product) *
|
||||
props.row.list.length -
|
||||
(taskProduct.find(
|
||||
(v) => v.productId === props.row.product.id,
|
||||
)?.discount || 0)) *
|
||||
(config?.vat || 0.07),
|
||||
)
|
||||
props.row.product.serviceChargeCalcVat
|
||||
? ((calcPricePerUnit(props.row.product) *
|
||||
props.row.list.length *
|
||||
(1 + (config?.vat || 0.07)) -
|
||||
taskProduct.find(
|
||||
(v) => v.productId === props.row.product.id,
|
||||
)?.discount || 0) /
|
||||
(1 + (config?.vat || 0.07))) *
|
||||
0.07
|
||||
: 0,
|
||||
2,
|
||||
)
|
||||
}}
|
||||
</q-td>
|
||||
|
|
|
|||
|
|
@ -128,32 +128,33 @@ function getPrice(
|
|||
})[];
|
||||
}[],
|
||||
) {
|
||||
return list.reduce(
|
||||
const value = list.reduce(
|
||||
(a, c) => {
|
||||
const pricePerUnit = c.product.serviceCharge;
|
||||
const vatFactor = c.product.serviceChargeCalcVat
|
||||
? (config.value?.vat ?? 0.07)
|
||||
: 0;
|
||||
|
||||
const pricePerUnit =
|
||||
precisionRound(c.product.serviceCharge * (1 + vatFactor)) /
|
||||
(1 + vatFactor);
|
||||
|
||||
const amount = c.list.length;
|
||||
const discount =
|
||||
taskProduct.value.find((v) => v.productId === c.product.id)?.discount ||
|
||||
0;
|
||||
const priceNoVat = c.product.serviceChargeVatIncluded
|
||||
? pricePerUnit / (1 + (config.value?.vat || 0.07))
|
||||
: pricePerUnit;
|
||||
const adjustedPriceWithVat = precisionRound(
|
||||
priceNoVat * (1 + (config.value?.vat || 0.07)),
|
||||
);
|
||||
const adjustedPriceNoVat =
|
||||
adjustedPriceWithVat / (1 + (config.value?.vat || 0.07));
|
||||
const priceDiscountNoVat = adjustedPriceNoVat * amount - discount;
|
||||
|
||||
const rawVatTotal = priceDiscountNoVat * (config.value?.vat || 0.07);
|
||||
const price =
|
||||
(pricePerUnit * amount * (1 + vatFactor) - discount) / (1 + vatFactor);
|
||||
|
||||
a.totalPrice = a.totalPrice + priceDiscountNoVat;
|
||||
a.totalDiscount = a.totalDiscount + Number(discount);
|
||||
a.vat = c.product.serviceChargeCalcVat ? a.vat + rawVatTotal : a.vat;
|
||||
const vat = price * vatFactor;
|
||||
|
||||
a.totalPrice = precisionRound(a.totalPrice + price + discount);
|
||||
a.totalDiscount = precisionRound(a.totalDiscount + discount);
|
||||
a.vat = precisionRound(a.vat + vat);
|
||||
a.vatExcluded = c.product.serviceChargeCalcVat
|
||||
? a.vatExcluded
|
||||
: precisionRound(a.vatExcluded + priceDiscountNoVat);
|
||||
a.finalPrice = a.totalPrice - a.totalDiscount + a.vat;
|
||||
: precisionRound(a.vatExcluded + price);
|
||||
a.finalPrice = precisionRound(a.totalPrice - a.totalDiscount + a.vat);
|
||||
return a;
|
||||
},
|
||||
{
|
||||
|
|
@ -164,6 +165,8 @@ function getPrice(
|
|||
finalPrice: 0,
|
||||
},
|
||||
);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function getTemplateData(
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import {
|
|||
EditButton,
|
||||
UndoButton,
|
||||
} from 'src/components/button';
|
||||
import { precisionRound } from 'src/utils/arithmetic';
|
||||
import { DebitNote, useDebitNote } from 'src/stores/debit-note';
|
||||
import { RequestWork } from 'src/stores/request-list/types';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
|
@ -40,6 +41,8 @@ import { useI18n } from 'vue-i18n';
|
|||
import { QForm } from 'quasar';
|
||||
import { getName } from 'src/services/keycloak';
|
||||
|
||||
import { RequestWorkStatus } from 'src/stores/request-list/types';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const creditNote = useCreditNote();
|
||||
|
|
@ -49,6 +52,7 @@ const configStore = useConfigStore();
|
|||
const { data: config } = storeToRefs(configStore);
|
||||
const { t } = useI18n();
|
||||
|
||||
const agentPrice = ref<boolean>(false);
|
||||
const refForm = ref<InstanceType<typeof QForm>>();
|
||||
const creditNoteData = ref<CreditNote>();
|
||||
const quotationData = ref<DebitNote | QuotationFull>();
|
||||
|
|
@ -206,22 +210,23 @@ function getPrice(
|
|||
) {
|
||||
return list.reduce(
|
||||
(a, c) => {
|
||||
const pricePerUnit =
|
||||
c.product.pricePerUnit - c.product.discount / c.product.amount;
|
||||
const amount = c.list.length;
|
||||
const priceNoVat = pricePerUnit;
|
||||
const priceDiscountNoVat = priceNoVat * amount;
|
||||
const value = creditNote.getfinalPriceCredit(c.list || []);
|
||||
|
||||
const rawVatTotal = priceDiscountNoVat * (config.value?.vat || 0.07);
|
||||
a.totalPrice = precisionRound(a.totalPrice + value.totalPrice);
|
||||
a.totalDiscount = precisionRound(a.totalDiscount + value.totalDiscount);
|
||||
a.vat = precisionRound(a.vat + value.vat);
|
||||
a.vatIncluded = precisionRound(a.vatIncluded + value.vatIncluded);
|
||||
a.vatExcluded = precisionRound(a.vatExcluded + value.vatExcluded);
|
||||
a.finalPrice = precisionRound(a.finalPrice + value.finalPrice);
|
||||
|
||||
a.totalPrice = a.totalPrice + priceDiscountNoVat;
|
||||
a.vat = c.product.vat !== 0 ? a.vat + rawVatTotal : a.vat;
|
||||
a.finalPrice = a.totalPrice + a.vat;
|
||||
return a;
|
||||
},
|
||||
{
|
||||
totalPrice: 0,
|
||||
totalDiscount: 0,
|
||||
vat: 0,
|
||||
vatIncluded: 0,
|
||||
vatExcluded: 0,
|
||||
finalPrice: 0,
|
||||
},
|
||||
);
|
||||
|
|
@ -296,6 +301,8 @@ async function getQuotation() {
|
|||
if (!ret) return;
|
||||
|
||||
quotationData.value = ret;
|
||||
|
||||
agentPrice.value = quotationData.value.agentPrice;
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
|
|
|
|||
|
|
@ -237,30 +237,12 @@ onMounted(async () => {
|
|||
});
|
||||
|
||||
function calcPricePerUnit(product: RequestWork['productService']['product']) {
|
||||
return product.vatIncluded
|
||||
? (agentPrice.value ? product.agentPrice : product.price) /
|
||||
(1 + (config.value?.vat || 0.07))
|
||||
: agentPrice.value
|
||||
? product.agentPrice
|
||||
: product.price;
|
||||
return agentPrice.value ? product.agentPrice : product.price;
|
||||
}
|
||||
|
||||
function calcPrice(
|
||||
product: RequestWork['productService']['product'],
|
||||
amount: number,
|
||||
vat: number = 0,
|
||||
) {
|
||||
const pricePerUnit = agentPrice.value ? product.agentPrice : product.price;
|
||||
|
||||
const priceNoVat = product.vatIncluded
|
||||
? pricePerUnit / (1 + (config.value?.vat || 0.07))
|
||||
: pricePerUnit;
|
||||
const priceDiscountNoVat = priceNoVat * amount - 0;
|
||||
|
||||
const rawVatTotal =
|
||||
vat === 0 ? 0 : priceDiscountNoVat * (config.value?.vat || 0.07);
|
||||
|
||||
return precisionRound(priceNoVat * amount + rawVatTotal);
|
||||
function calcPrice(c: RequestWork[]): number {
|
||||
const price = creditNoteStore.getfinalPriceCredit(c);
|
||||
return price.finalPrice;
|
||||
}
|
||||
|
||||
watch(elements, () => {});
|
||||
|
|
@ -327,31 +309,15 @@ function closeAble() {
|
|||
<th>{{ $t('preview.pricePerUnit') }}</th>
|
||||
<th>{{ $t('preview.value') }}</th>
|
||||
</tr>
|
||||
{{ console.log(chunks) }}
|
||||
{{ console.log(chunk) }}
|
||||
<tr v-for="(v, i) in chunk" :key="i">
|
||||
<td class="text-center">{{ i + 1 }}</td>
|
||||
<td>{{ v.product.product.code }}</td>
|
||||
<td>{{ v.product.product.name }}</td>
|
||||
<td style="text-align: center">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
calcPricePerUnit(v.product.product) +
|
||||
(v.product.product.calcVat
|
||||
? calcPricePerUnit(v.product.product) *
|
||||
(config?.vat || 0.07)
|
||||
: 0),
|
||||
2,
|
||||
)
|
||||
}}
|
||||
<td style="text-align: right">
|
||||
{{ formatNumberDecimal(calcPricePerUnit(v.product.product), 2) }}
|
||||
</td>
|
||||
<td style="text-align: center">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
calcPrice(v.product.product, v.list.length, v.product.vat),
|
||||
2,
|
||||
)
|
||||
}}
|
||||
<td style="text-align: right">
|
||||
{{ formatNumberDecimal(calcPrice(v.list), 2) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
@ -409,8 +375,8 @@ function closeAble() {
|
|||
{{
|
||||
formatNumberDecimal(
|
||||
summaryPrice.totalPrice -
|
||||
summaryPrice.totalDiscount +
|
||||
summaryPrice.vat,
|
||||
summaryPrice.totalDiscount -
|
||||
summaryPrice.vatExcluded,
|
||||
2,
|
||||
)
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@ function titleMode(mode: View): string {
|
|||
})
|
||||
}}
|
||||
</span>
|
||||
<span>เลขประจำตัวผู้เสียภาษี {{ branch.taxNo }}</span>
|
||||
<span>เบอร์โทร {{ branch.telephoneNo }}</span>
|
||||
<span>{{ $t('branch.form.taxNo') }} {{ branch.taxNo }}</span>
|
||||
<span>{{ $t('taskOrder.telephone') }} {{ branch.telephoneNo }}</span>
|
||||
<span>{{ branch.webUrl }}</span>
|
||||
</article>
|
||||
<article>
|
||||
|
|
@ -105,8 +105,18 @@ function titleMode(mode: View): string {
|
|||
})
|
||||
}}
|
||||
</span>
|
||||
<span>เลขประจำตัวผู้เสียภาษี {{ customer.citizenId }}</span>
|
||||
<span>เบอร์โทร {{ customer.telephoneNo }}</span>
|
||||
<span>
|
||||
{{
|
||||
customer.customer.customerType === 'CORP'
|
||||
? `${$t('customer.form.legalPersonNo')} `
|
||||
: `${$t('customer.form.taxpayyerNo')} `
|
||||
}}{{
|
||||
customer.customer.customerType === 'CORP'
|
||||
? customer.codeCustomer
|
||||
: customer.citizenId
|
||||
}}
|
||||
</span>
|
||||
<span>{{ $t('taskOrder.telephone') }} {{ customer.telephoneNo }}</span>
|
||||
</article>
|
||||
</section>
|
||||
<section class="detail-quotation-info">
|
||||
|
|
|
|||
|
|
@ -12,10 +12,13 @@ import { baseUrl, formatNumberDecimal } from 'src/stores/utils';
|
|||
import { precisionRound } from 'src/utils/arithmetic';
|
||||
import { productColumn } from '../constants';
|
||||
|
||||
import { useCreditNote } from 'src/stores/credit-note';
|
||||
|
||||
const configStore = useConfigStore();
|
||||
const { data: config } = storeToRefs(configStore);
|
||||
|
||||
const currentExpanded = ref<boolean[]>([]);
|
||||
const creditNote = useCreditNote();
|
||||
|
||||
defineProps<{
|
||||
readonly?: boolean;
|
||||
|
|
@ -44,15 +47,22 @@ function calcPricePerUnit(product: RequestWork['productService']) {
|
|||
return product.pricePerUnit - product.discount / product.amount;
|
||||
}
|
||||
|
||||
function calcPrice(c: RequestWork['productService'], amount: number) {
|
||||
const pricePerUnit = c.pricePerUnit - c.discount / c.amount;
|
||||
const priceNoVat = pricePerUnit;
|
||||
const priceDiscountNoVat = priceNoVat * amount;
|
||||
function calcVat(c: RequestWork['productService']) {
|
||||
const vatFactor = c.product.serviceChargeCalcVat
|
||||
? (config.value?.vat ?? 0.07)
|
||||
: 0;
|
||||
|
||||
const rawVatTotal =
|
||||
c.vat === 0 ? 0 : priceDiscountNoVat * (config.value?.vat || 0.07);
|
||||
const price = precisionRound(
|
||||
c.pricePerUnit * (1 + vatFactor) - c.discount / (1 + vatFactor),
|
||||
);
|
||||
|
||||
return precisionRound(priceNoVat * amount + rawVatTotal);
|
||||
const vat = (price / (1 + vatFactor)) * vatFactor;
|
||||
return vat;
|
||||
}
|
||||
|
||||
function calcPrice(c: RequestWork[]) {
|
||||
const price = creditNote.getfinalPriceCredit(c);
|
||||
return price.finalPrice;
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
|
|
@ -162,12 +172,7 @@ function calcPrice(c: RequestWork['productService'], amount: number) {
|
|||
|
||||
<!-- total -->
|
||||
<q-td class="text-right">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
calcPrice(props.row.product, props.row.list.length),
|
||||
2,
|
||||
)
|
||||
}}
|
||||
{{ formatNumberDecimal(calcPrice(props.row.list), 2) }}
|
||||
</q-td>
|
||||
<q-td>
|
||||
<q-btn
|
||||
|
|
|
|||
|
|
@ -386,7 +386,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 +412,7 @@ async function assignFormData(id: string) {
|
|||
quotationId: data.debitNoteQuotationId,
|
||||
remark: data.remark || undefined,
|
||||
}),
|
||||
(currentFormData.value = structuredClone(previousValue));
|
||||
(currentFormData.value = structuredClone(previousValue)));
|
||||
|
||||
assignProductServiceList();
|
||||
assignSelectedWorker();
|
||||
|
|
@ -434,6 +434,7 @@ async function getQuotation(id?: string) {
|
|||
const data = await quotationStore.getQuotation(quotationId);
|
||||
if (!!data) {
|
||||
quotationData.value = data;
|
||||
agentPrice.value = quotationData.value.agentPrice;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -539,6 +540,8 @@ function getPrice(
|
|||
) {
|
||||
if (filterHook) list = list.filter(filterHook);
|
||||
|
||||
console.log(list);
|
||||
|
||||
return list.reduce(
|
||||
(a, c) => {
|
||||
if (
|
||||
|
|
@ -549,25 +552,28 @@ function getPrice(
|
|||
return a;
|
||||
}
|
||||
|
||||
const price = precisionRound(c.pricePerUnit * c.amount);
|
||||
const vat =
|
||||
precisionRound(
|
||||
(c.pricePerUnit * (c.discount ? c.amount : 1) - c.discount) *
|
||||
(config.value?.vat || 0.07),
|
||||
) * (!c.discount ? c.amount : 1);
|
||||
const calcVat =
|
||||
c.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat'];
|
||||
|
||||
a.totalPrice = precisionRound(a.totalPrice + price);
|
||||
a.totalDiscount = precisionRound(a.totalDiscount + Number(c.discount));
|
||||
a.vat = c.product.calcVat ? precisionRound(a.vat + vat) : a.vat;
|
||||
const vatFactor = calcVat ? (config.value?.vat ?? 0.07) : 0;
|
||||
|
||||
const pricePerUnit = precisionRound(
|
||||
(c.pricePerUnit * (1 + vatFactor)) / (1 + vatFactor),
|
||||
);
|
||||
|
||||
const price = precisionRound(
|
||||
(pricePerUnit * c.amount * (1 + vatFactor) - c.discount) /
|
||||
(1 + vatFactor),
|
||||
);
|
||||
const vat = price * vatFactor;
|
||||
|
||||
a.totalPrice = precisionRound(a.totalPrice + price + c.discount);
|
||||
a.totalDiscount = precisionRound(a.totalDiscount + c.discount);
|
||||
a.vat = precisionRound(a.vat + vat);
|
||||
a.vatExcluded = c.product.calcVat
|
||||
? a.vatExcluded
|
||||
: precisionRound(a.vat + vat);
|
||||
a.finalPrice = precisionRound(
|
||||
a.totalPrice -
|
||||
a.totalDiscount +
|
||||
a.vat -
|
||||
Number(currentFormData.value.discount || 0),
|
||||
);
|
||||
: precisionRound(a.vatExcluded + price);
|
||||
a.finalPrice = precisionRound(a.totalPrice - a.totalDiscount + a.vat);
|
||||
|
||||
return a;
|
||||
},
|
||||
|
|
@ -575,6 +581,7 @@ function getPrice(
|
|||
totalPrice: 0,
|
||||
totalDiscount: 0,
|
||||
vat: 0,
|
||||
vatIncluded: 0,
|
||||
vatExcluded: 0,
|
||||
finalPrice: 0,
|
||||
},
|
||||
|
|
@ -869,6 +876,7 @@ async function exampleReceipt(id: string) {
|
|||
|
||||
function storeDataLocal() {
|
||||
// quotationFormData.value.productServiceList = productServiceList.value;
|
||||
//
|
||||
|
||||
localStorage.setItem(
|
||||
'debit-note-preview',
|
||||
|
|
@ -877,6 +885,7 @@ function storeDataLocal() {
|
|||
...currentFormData.value,
|
||||
customerBranchId: quotationData.value?.customerBranchId,
|
||||
registeredBranchId: quotationData.value?.registeredBranchId,
|
||||
agentPrice: quotationData.value.agentPrice,
|
||||
},
|
||||
meta: {
|
||||
source: {
|
||||
|
|
|
|||
|
|
@ -411,7 +411,7 @@ watch(
|
|||
@delete="() => triggerDelete(item.row.id)"
|
||||
:title="item.row.debitNoteQuotation?.workName"
|
||||
:code="item.row.code"
|
||||
:status="$t(`quotation.status.${item.row.quotationStatus}`)"
|
||||
:status="$t(`debitNote.stats.${item.row.quotationStatus}`)"
|
||||
:badge-color="hslaColors[item.row.quotationStatus] || ''"
|
||||
:custom-data="[
|
||||
{
|
||||
|
|
|
|||
|
|
@ -82,6 +82,8 @@ const data = ref<
|
|||
>();
|
||||
|
||||
const productServiceList = ref<ProductServiceList[]>([]);
|
||||
const selectedInstallmentNo = ref<number[]>([]);
|
||||
const agentPrice = ref<boolean>(false);
|
||||
|
||||
const summaryPrice = ref<SummaryPrice>({
|
||||
totalPrice: 0,
|
||||
|
|
@ -217,6 +219,8 @@ onMounted(async () => {
|
|||
}
|
||||
|
||||
productServiceList.value = parsed.meta.productServicelist;
|
||||
selectedInstallmentNo.value = parsed.meta.selectedInstallmentNo;
|
||||
agentPrice.value = parsed.meta.agentPrice;
|
||||
|
||||
productList.value =
|
||||
productServiceList.value?.map((v) => ({
|
||||
|
|
@ -224,40 +228,56 @@ onMounted(async () => {
|
|||
code: v.product.code,
|
||||
detail: v.product.name,
|
||||
amount: v.amount || 0,
|
||||
priceUnit: v.pricePerUnit || 0,
|
||||
priceUnit:
|
||||
v.pricePerUnit +
|
||||
(v.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat']
|
||||
? v.pricePerUnit * (config.value?.vat || 0.07)
|
||||
: 0),
|
||||
discount: v.discount || 0,
|
||||
vat: v.vat || 0,
|
||||
value: precisionRound(
|
||||
(v.pricePerUnit || 0) * v.amount -
|
||||
(v.discount || 0) +
|
||||
(v.product.calcVat
|
||||
? ((v.pricePerUnit || 0) * v.amount - (v.discount || 0)) *
|
||||
(config.value?.vat || 0.07)
|
||||
: 0),
|
||||
(v.pricePerUnit +
|
||||
(v.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat']
|
||||
? v.pricePerUnit * (config.value?.vat || 0.07)
|
||||
: 0)) *
|
||||
v.amount -
|
||||
v.discount,
|
||||
),
|
||||
})) || [];
|
||||
}
|
||||
|
||||
summaryPrice.value = (productServiceList.value || []).reduce(
|
||||
(a, c) => {
|
||||
const price = precisionRound((c.pricePerUnit || 0) * c.amount);
|
||||
const vat = precisionRound(
|
||||
((c.pricePerUnit || 0) * c.amount - (c.discount || 0)) *
|
||||
(config.value?.vat || 0.07),
|
||||
if (
|
||||
selectedInstallmentNo.value?.length > 0 &&
|
||||
c.installmentNo &&
|
||||
!selectedInstallmentNo.value?.includes(c.installmentNo)
|
||||
) {
|
||||
return a;
|
||||
}
|
||||
|
||||
const calcVat =
|
||||
c.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat'];
|
||||
|
||||
const vatFactor = calcVat ? (config.value?.vat ?? 0.07) : 0;
|
||||
|
||||
const pricePerUnit = precisionRound(
|
||||
(c.pricePerUnit * (1 + vatFactor)) / (1 + vatFactor),
|
||||
);
|
||||
|
||||
a.totalPrice = precisionRound(a.totalPrice + price);
|
||||
a.totalDiscount = precisionRound(a.totalDiscount + Number(c.discount));
|
||||
a.vat = c.product.calcVat ? precisionRound(a.vat + vat) : a.vat;
|
||||
const price = precisionRound(
|
||||
(pricePerUnit * c.amount * (1 + vatFactor) - c.discount) /
|
||||
(1 + vatFactor),
|
||||
);
|
||||
const vat = price * vatFactor;
|
||||
|
||||
a.totalPrice = precisionRound(a.totalPrice + price + c.discount);
|
||||
a.totalDiscount = precisionRound(a.totalDiscount + c.discount);
|
||||
a.vat = precisionRound(a.vat + vat);
|
||||
a.vatExcluded = c.product.calcVat
|
||||
? a.vatExcluded
|
||||
: precisionRound(a.vat + vat);
|
||||
a.finalPrice = precisionRound(
|
||||
a.totalPrice -
|
||||
a.totalDiscount +
|
||||
a.vat -
|
||||
Number(data.value?.discount || 0),
|
||||
);
|
||||
: precisionRound(a.vatExcluded + price);
|
||||
a.finalPrice = precisionRound(a.totalPrice - a.totalDiscount + a.vat);
|
||||
|
||||
return a;
|
||||
},
|
||||
|
|
@ -349,7 +369,9 @@ function print() {
|
|||
{{ formatNumberDecimal(v.priceUnit, 2) }}
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
{{ formatNumberDecimal(v.discount, 2) }}
|
||||
<template v-if="v.discount !== 0">
|
||||
{{ formatNumberDecimal(v.discount, 2) }} ฿
|
||||
</template>
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
{{ formatNumberDecimal(v.vat, 2) }}
|
||||
|
|
@ -413,8 +435,8 @@ function print() {
|
|||
{{
|
||||
formatNumberDecimal(
|
||||
summaryPrice.totalPrice -
|
||||
summaryPrice.totalDiscount +
|
||||
summaryPrice.vat,
|
||||
summaryPrice.totalDiscount -
|
||||
summaryPrice.vatExcluded,
|
||||
2,
|
||||
)
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@ function titleMode(mode: View): string {
|
|||
})
|
||||
}}
|
||||
</span>
|
||||
<span>เลขประจำตัวผู้เสียภาษี {{ branch.taxNo }}</span>
|
||||
<span>เบอร์โทร {{ branch.telephoneNo }}</span>
|
||||
<span>{{ $t('branch.form.taxNo') }} {{ branch.taxNo }}</span>
|
||||
<span>{{ $t('taskOrder.telephone') }} {{ branch.telephoneNo }}</span>
|
||||
<span>{{ branch.webUrl }}</span>
|
||||
</article>
|
||||
<article>
|
||||
|
|
@ -105,8 +105,18 @@ function titleMode(mode: View): string {
|
|||
})
|
||||
}}
|
||||
</span>
|
||||
<span>เลขประจำตัวผู้เสียภาษี {{ customer.citizenId }}</span>
|
||||
<span>เบอร์โทร {{ customer.telephoneNo }}</span>
|
||||
<span>
|
||||
{{
|
||||
customer.customer.customerType === 'CORP'
|
||||
? `${$t('customer.form.legalPersonNo')} `
|
||||
: `${$t('customer.form.taxpayyerNo')} `
|
||||
}}{{
|
||||
customer.customer.customerType === 'CORP'
|
||||
? customer.codeCustomer
|
||||
: customer.citizenId
|
||||
}}
|
||||
</span>
|
||||
<span>{{ $t('taskOrder.telephone') }} {{ customer.telephoneNo }}</span>
|
||||
</article>
|
||||
</section>
|
||||
<section class="detail-quotation-info">
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ import { defineStore } from 'pinia';
|
|||
|
||||
import { api } from 'src/boot/axios';
|
||||
import { PaginationResult } from 'src/types';
|
||||
import { RequestWork, RequestWorkStatus } from 'src/stores/request-list/types';
|
||||
import { precisionRound } from 'src/utils/arithmetic';
|
||||
import { useConfigStore } from 'src/stores/config';
|
||||
|
||||
import { manageAttachment, manageFile } from '../utils';
|
||||
|
||||
const ENDPOINT = 'credit-note';
|
||||
|
|
@ -80,6 +84,7 @@ export async function acceptCreditNote(id: string) {
|
|||
}
|
||||
|
||||
export const useCreditNote = defineStore('credit-note-store', () => {
|
||||
const configStore = useConfigStore();
|
||||
const data = ref<Data[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const pageMax = ref<number>(1);
|
||||
|
|
@ -90,6 +95,75 @@ export const useCreditNote = defineStore('credit-note-store', () => {
|
|||
[Status.Success]: 0,
|
||||
});
|
||||
|
||||
function getfinalPriceCredit(c: RequestWork[]): {
|
||||
totalPrice: number;
|
||||
totalDiscount: number;
|
||||
vat: number;
|
||||
vatIncluded: number;
|
||||
vatExcluded: number;
|
||||
finalPrice: number;
|
||||
} {
|
||||
const price = c.reduce(
|
||||
(acc, crr) => {
|
||||
const servicesChargeStepCount =
|
||||
crr.productService.work?.productOnWork?.find(
|
||||
(item) => item.productId === crr.productService.productId,
|
||||
).stepCount;
|
||||
|
||||
const successCount = crr.stepStatus.filter(
|
||||
(item) => item.workStatus === RequestWorkStatus.Completed,
|
||||
).length;
|
||||
|
||||
const vatFactor =
|
||||
crr.productService.vat > 0 ? (configStore.data?.vat ?? 0.07) : 0;
|
||||
|
||||
const price = precisionRound(
|
||||
crr.productService.pricePerUnit * (1 + vatFactor) -
|
||||
crr.productService.discount,
|
||||
);
|
||||
|
||||
const vat = crr.productService.product.calcVat
|
||||
? (price / (1 + vatFactor)) * vatFactor
|
||||
: 0;
|
||||
|
||||
acc.totalPrice = precisionRound(
|
||||
acc.totalPrice + price + crr.productService.discount,
|
||||
);
|
||||
acc.totalDiscount = precisionRound(
|
||||
acc.totalDiscount + crr.productService.discount,
|
||||
);
|
||||
acc.vat = precisionRound(acc.vat + vat);
|
||||
acc.vatExcluded = crr.productService.product.agentPriceCalcVat
|
||||
? acc.vatExcluded
|
||||
: precisionRound(acc.vatExcluded + price / (1 + vatFactor));
|
||||
|
||||
if (servicesChargeStepCount && successCount) {
|
||||
acc.finalPrice = precisionRound(
|
||||
acc.finalPrice +
|
||||
price -
|
||||
crr.productService.product.serviceCharge * successCount,
|
||||
);
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
acc.finalPrice = precisionRound(acc.finalPrice + price);
|
||||
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
totalPrice: 0,
|
||||
totalDiscount: 0,
|
||||
vat: 0,
|
||||
vatIncluded: 0,
|
||||
vatExcluded: 0,
|
||||
finalPrice: 0,
|
||||
},
|
||||
);
|
||||
|
||||
return price;
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
page,
|
||||
|
|
@ -97,6 +171,8 @@ export const useCreditNote = defineStore('credit-note-store', () => {
|
|||
pageSize,
|
||||
stats,
|
||||
|
||||
getfinalPriceCredit,
|
||||
|
||||
getCreditNoteStats,
|
||||
getCreditNote,
|
||||
getCreditNoteList,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
CitizenPayload,
|
||||
} from './types';
|
||||
import { Employee } from '../employee/types';
|
||||
import { getToken } from 'src/services/keycloak';
|
||||
import { baseUrl, manageAttachment, manageFile, manageMeta } from '../utils';
|
||||
|
||||
const useCustomerStore = defineStore('api-customer', () => {
|
||||
|
|
@ -114,6 +115,10 @@ const useCustomerStore = defineStore('api-customer', () => {
|
|||
customerType?: CustomerType;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
businessType?: string;
|
||||
province?: string;
|
||||
district?: string;
|
||||
subDistrict?: string;
|
||||
},
|
||||
Data extends Pagination<
|
||||
(Customer &
|
||||
|
|
@ -472,6 +477,56 @@ const useCustomerStore = defineStore('api-customer', () => {
|
|||
return false;
|
||||
}
|
||||
|
||||
async function customerExport(params: {
|
||||
customerType?: CustomerType;
|
||||
query?: string;
|
||||
status?: 'CREATED' | 'ACTIVE' | 'INACTIVE';
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
includeBranch?: boolean;
|
||||
company?: boolean;
|
||||
activeBranchOnly?: boolean;
|
||||
startDate?: string | Date;
|
||||
endDate?: string | Date;
|
||||
businessType?: string;
|
||||
province?: string;
|
||||
district?: string;
|
||||
subDistrict?: string;
|
||||
}) {
|
||||
let url = baseUrl + '/' + 'customer-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 {
|
||||
data,
|
||||
|
||||
|
|
@ -498,6 +553,8 @@ const useCustomerStore = defineStore('api-customer', () => {
|
|||
fetchBranchEmployee,
|
||||
|
||||
deleteAttachment,
|
||||
|
||||
customerExport,
|
||||
...attachmentManager,
|
||||
...fileManager,
|
||||
...metaManager,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
EmployeeVisaPayload,
|
||||
} from './types';
|
||||
import { CustomerBranch } from '../customer/types';
|
||||
import { getToken } from 'src/services/keycloak';
|
||||
import { baseUrl, manageAttachment, manageFile, manageMeta } from '../utils';
|
||||
|
||||
const useEmployeeStore = defineStore('api-employee', () => {
|
||||
|
|
@ -469,6 +470,55 @@ const useEmployeeStore = defineStore('api-employee', () => {
|
|||
return false;
|
||||
}
|
||||
|
||||
async function employeeExport(params: {
|
||||
zipCode?: string;
|
||||
gender?: string;
|
||||
status?: Status;
|
||||
visa?: boolean;
|
||||
passport?: boolean;
|
||||
customerId?: string;
|
||||
customerBranchId?: string;
|
||||
query?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
activeOnly?: boolean;
|
||||
startDate?: string | Date;
|
||||
endDate?: string | Date;
|
||||
}) {
|
||||
let url = baseUrl + '/' + 'employee-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 = 'employee-report' + '.csv';
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.click();
|
||||
a.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
globalOption,
|
||||
|
|
@ -512,6 +562,8 @@ const useEmployeeStore = defineStore('api-employee', () => {
|
|||
...attachmentManager,
|
||||
...fileManager,
|
||||
...metaManager,
|
||||
|
||||
employeeExport,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,12 @@ export type CustomerBranchRelation = {
|
|||
payDate: string;
|
||||
jobDescription: string;
|
||||
jobPosition: string;
|
||||
businessType: string;
|
||||
businessTypeId: string;
|
||||
businessType?: {
|
||||
id: string;
|
||||
name: string;
|
||||
nameEN: string;
|
||||
};
|
||||
contactName: string;
|
||||
officeTel: string;
|
||||
contactTel: string;
|
||||
|
|
@ -80,7 +85,6 @@ export type CustomerBranchRelation = {
|
|||
namePrefix: string;
|
||||
telephoneNo: string;
|
||||
codeCustomer: string;
|
||||
customerName: string;
|
||||
updatedByUserId: string;
|
||||
createdByUserId: string;
|
||||
code: string;
|
||||
|
|
@ -174,6 +178,7 @@ type WorkRelation = {
|
|||
createdByUserId?: string;
|
||||
updatedAt: string;
|
||||
updatedByUserId?: string;
|
||||
productOnWork?: { productId: string; stepCount: number }[];
|
||||
};
|
||||
|
||||
type ServiceRelation = {
|
||||
|
|
@ -417,6 +422,9 @@ export type QuotationPaymentData = {
|
|||
date: string;
|
||||
id: string;
|
||||
code: string;
|
||||
account?: string;
|
||||
channel?: string;
|
||||
reference?: string;
|
||||
};
|
||||
|
||||
export type Details = {
|
||||
|
|
@ -435,6 +443,9 @@ export type PaymentPayload = {
|
|||
paymentStatus: string;
|
||||
date: Date;
|
||||
amount: number;
|
||||
account?: string;
|
||||
channel?: string;
|
||||
reference?: string;
|
||||
};
|
||||
|
||||
export type ProductServiceList = {
|
||||
|
|
|
|||
|
|
@ -55,9 +55,12 @@ export function calculatePrice(
|
|||
// Calculate before VAT
|
||||
const beforeVat = vatIncluded ? precisionRound(price / (1 + vat)) : price;
|
||||
|
||||
// Return based on output selection
|
||||
if (output === 'vat') return calculatedVat;
|
||||
if (output === 'total') return total;
|
||||
if (output === 'beforeVat') return beforeVat;
|
||||
return 0;
|
||||
switch (output) {
|
||||
case 'vat':
|
||||
return calculatedVat;
|
||||
case 'total':
|
||||
return total;
|
||||
case 'beforeVat':
|
||||
return beforeVat;
|
||||
}
|
||||
}
|
||||
|
|
|
|||