feat: enhance credit note functionality and improve task order handling

This commit is contained in:
puriphatt 2025-02-24 16:20:56 +07:00
parent 91c7db7c7b
commit dc225ac31b
3 changed files with 125 additions and 36 deletions

View file

@ -23,7 +23,9 @@ const emit = defineEmits<{
const props = defineProps<{
taskListGroup?: {
product: RequestWork['productService']['product'];
product:
| RequestWork['productService']['product']
| RequestWork['productService'];
list: (RequestWork & {
_template?: {
id: string;
@ -135,6 +137,7 @@ async function getList() {
}
function getStep(requestWork: RequestWork) {
if (!requestWork.stepStatus) return 0;
const target = requestWork.stepStatus.find(
(v) =>
v.workStatus === RequestWorkStatus.Ready ||
@ -166,7 +169,7 @@ function submit() {
requestWorkStep?: Task;
}[] = [];
selectedEmployee.value.forEach((v, i) => {
if (v.stepStatus.length === 0) {
if (!v.stepStatus || v.stepStatus.length === 0) {
selected.push({
step: 0,
requestWorkId: v.id || '',
@ -224,7 +227,7 @@ function close() {
function onDialogOpen() {
// assign selected to group
!props.creditNote && assignTempGroup();
assignTempGroup();
// match group to check
selectedEmployee.value = [];
@ -232,7 +235,7 @@ function onDialogOpen() {
const matchingItems = tempGroupEdit.value
.flatMap((g) => g.list)
.filter((l) => {
if (l.stepStatus.length === 0) {
if (!l.stepStatus || l.stepStatus.length === 0) {
return taskList.value.some(
(t) => t.requestWorkStep?.requestWork.id === l.id,
);
@ -248,8 +251,12 @@ function onDialogOpen() {
function assignTempGroup() {
if (!props.taskListGroup) return;
props.taskListGroup.forEach((newGroup) => {
const productId = props.creditNote
? (newGroup.product as RequestWork['productService']).productId
: (newGroup.product as RequestWork['productService']['product']).id;
const existingGroup = tempGroupEdit.value.find(
(g) => g.product.id === newGroup.product.id,
(g) => g.product.id === productId,
);
if (existingGroup) {
@ -260,7 +267,9 @@ function assignTempGroup() {
});
} else {
tempGroupEdit.value.push({
...newGroup,
product: props.creditNote
? (newGroup.product as RequestWork['productService']).product
: (newGroup.product as RequestWork['productService']['product']),
list: [...newGroup.list], // Ensure a new reference
});
}

View file

@ -24,7 +24,13 @@ import SelectReadyRequestWork from '../09_task-order/SelectReadyRequestWork.vue'
import RefundInformation from './RefundInformation.vue';
import QuotationFormReceipt from '../05_quotation/QuotationFormReceipt.vue';
import DialogViewFile from 'src/components/dialog/DialogViewFile.vue';
import { MainButton, SaveButton } from 'src/components/button';
import {
MainButton,
SaveButton,
CancelButton,
EditButton,
UndoButton,
} from 'src/components/button';
import { RequestWork } from 'src/stores/request-list/types';
import { storeToRefs } from 'pinia';
import useOptionStore from 'src/stores/options';
@ -76,15 +82,16 @@ const statusTabForm = ref<
}[]
>([]);
const readonly = computed(
() =>
creditNoteData.value?.creditNoteStatus === CreditNoteStatus.Pending ||
creditNoteData.value?.creditNoteStatus === CreditNoteStatus.Success,
);
// const readonly = computed(
// () =>
// creditNoteData.value?.creditNoteStatus === CreditNoteStatus.Pending ||
// creditNoteData.value?.creditNoteStatus === CreditNoteStatus.Success,
// );
const pageState = reactive({
productDialog: false,
fileDialog: false,
mode: 'view' as 'view' | 'edit' | 'info',
});
const defaultRemark = '#[quotation-labor]<br/><br/>#[quotation-payment]';
@ -161,9 +168,11 @@ async function initStatus() {
{
title: 'Pending',
status: creditNoteData.value?.id
? creditNoteData.value.creditNoteStatus === CreditNoteStatus.Success
? 'done'
: 'doing'
? creditNoteData.value.creditNoteStatus === CreditNoteStatus.Waiting
? 'waiting'
: creditNoteData.value.creditNoteStatus === CreditNoteStatus.Success
? 'done'
: 'doing'
: 'waiting',
active: () => view.value === CreditNoteStatus.Pending,
handler: async () => {
@ -253,8 +262,9 @@ function assignFormData() {
requestWorkStep: {
requestWork: {
...v,
stepStatus: v.stepStatus || [],
request: { ...v.request, quotation: current.quotation },
},
} as RequestWork,
},
}));
}
@ -285,12 +295,24 @@ async function getQuotation() {
async function submit() {
const payload = formData.value;
payload.requestWorkId = formTaskList.value.map((v) => v.requestWorkId);
payload.quotationId =
typeof route.query['quotationId'] === 'string'
? route.query['quotationId']
: '';
const res = await creditNote.createCreditNote(payload);
(pageState.mode === 'edit'
? creditNoteData.value?.quotationId
: typeof route.query['quotationId'] === 'string'
? route.query['quotationId']
: '') || '';
const res =
pageState.mode === 'edit'
? await creditNote.updateCreditNote(
creditNoteData.value?.id || '',
payload,
)
: creditNoteData.value
? await creditNote.acceptCreditNote(creditNoteData.value.id)
: await creditNote.createCreditNote(payload);
if (res) {
await router.push(`/credit-note/${res.id}`);
@ -302,6 +324,7 @@ async function submit() {
}
initStatus();
pageState.mode = 'info';
}
}
@ -526,13 +549,31 @@ function storeDataLocal() {
window.open(url, '_blank');
}
function closeTab() {
dialogWarningClose(t, {
message: t('dialog.message.close'),
action: () => {
window.close();
},
cancel: () => {},
});
}
function undo() {
assignFormData();
pageState.mode = 'info';
}
onMounted(async () => {
initTheme();
initLang();
await useConfigStore().getConfig();
await getCreditNote();
await getQuotation();
creditNoteData.value && (await getFileList(creditNoteData.value.id, true));
if (creditNoteData.value) {
pageState.mode = 'info';
await getFileList(creditNoteData.value.id, true);
}
initStatus();
});
</script>
@ -587,7 +628,7 @@ onMounted(async () => {
:key="i.title"
:label="
$t(
`creditNote${i.title === 'title' ? '' : '.stats'}.${i.title}`,
`creditNote${i.title === 'title' ? '' : '.status'}.${i.title}`,
)
"
:status-active="i.active?.()"
@ -616,7 +657,7 @@ onMounted(async () => {
>
<CreditNoteExpansion
v-if="view === null"
:readonly="readonly"
:readonly="pageState.mode === 'info'"
v-model:reason="formData.reason"
v-model:detail="formData.detail"
/>
@ -625,7 +666,7 @@ onMounted(async () => {
<ProductExpansion
v-if="view === null"
creditNote
:readonly="readonly"
:readonly="pageState.mode === 'info'"
:agentPrice="quotationData?.agentPrice"
:task-list="taskListGroup"
@add-product="openProductDialog"
@ -633,7 +674,7 @@ onMounted(async () => {
<PaymentExpansion
v-if="view === null"
:readonly="readonly"
:readonly="pageState.mode === 'info'"
:total-price="summaryPrice.finalPrice"
v-model:payback-type="formData.paybackType"
v-model:payback-bank="formData.paybackBank"
@ -746,7 +787,7 @@ onMounted(async () => {
v-model:remark="formData.remark"
:default-remark="defaultRemark"
:items="[]"
:readonly
:readonly="pageState.mode === 'info'"
>
<template #hint>
{{ $t('general.hintRemark') }}
@ -793,7 +834,7 @@ onMounted(async () => {
<!-- SEC: footer -->
<footer class="surface-1 q-pa-md full-width">
<nav class="row justify-end">
<nav class="row justify-end" style="gap: var(--size-2)">
<!-- TODO: view example -->
<MainButton
class="q-mr-auto"
@ -804,16 +845,48 @@ onMounted(async () => {
>
{{ $t('general.view', { msg: $t('general.example') }) }}
</MainButton>
<!-- @click="submit" -->
<UndoButton v-if="pageState.mode === 'edit'" outlined @click="undo()" />
<CancelButton
v-if="
pageState.mode === 'info' &&
creditNoteData?.creditNoteStatus === CreditNoteStatus.Waiting
"
@click="closeTab()"
:label="$t('dialog.action.close')"
outlined
/>
<SaveButton
v-if="!readonly"
:disabled="taskListGroup.length === 0"
v-if="
!creditNoteData ||
creditNoteData?.creditNoteStatus === CreditNoteStatus.Waiting
"
:disabled="taskListGroup.length === 0 || pageState.mode === 'edit'"
type="submit"
@click.stop="(e) => refForm?.submit(e)"
:label="$t('creditNote.label.submit')"
:label="
$t(
`creditNote.label.${creditNoteData?.creditNoteStatus ? 'submit' : 'request'}`,
)
"
icon="mdi-account-multiple-check-outline"
solid
></SaveButton>
/>
<SaveButton
v-if="pageState.mode === 'edit'"
:disabled="taskListGroup.length === 0"
@click="(e) => refForm?.submit(e)"
solid
/>
<EditButton
v-if="
pageState.mode === 'info' &&
creditNoteData?.creditNoteStatus === CreditNoteStatus.Waiting
"
class="no-print"
@click="pageState.mode = 'edit'"
solid
/>
</nav>
</footer>
</div>
@ -822,6 +895,7 @@ onMounted(async () => {
<SelectReadyRequestWork
v-if="quotationData"
creditNote
:task-list-group="taskListGroup"
:fetch-params="{ cancelOnly: true, quotationId: quotationData.id }"
v-model:open="pageState.productDialog"
v-model:task-list="formTaskList"

View file

@ -35,7 +35,7 @@ const { stats, pageMax, page, data, pageSize } = storeToRefs(creditNote);
// NOTE: Variable
const pageState = reactive({
quotationId: '',
currentTab: CreditNoteStatus.Pending,
currentTab: CreditNoteStatus.Waiting,
hideStat: false,
statusFilter: 'None',
inputSearch: '',
@ -180,16 +180,22 @@ watch(
<StatCardComponent
labelI18n
:branch="[
{
icon: 'icon-park-outline:loading-one',
count: stats[CreditNoteStatus.Pending] || 0,
label: `creditNote.status.${CreditNoteStatus.Waiting}`,
color: 'light-yellow',
},
{
icon: 'material-symbols-light:receipt-long',
count: stats[CreditNoteStatus.Pending] || 0,
label: `creditNote.stats.${CreditNoteStatus.Pending}`,
label: `creditNote.status.${CreditNoteStatus.Pending}`,
color: 'orange',
},
{
icon: 'mdi-check-decagram-outline',
count: stats[CreditNoteStatus.Success] || 0,
label: `creditNote.stats.${CreditNoteStatus.Success}`,
label: `creditNote.status.${CreditNoteStatus.Success}`,
color: 'blue',
},
]"
@ -345,7 +351,7 @@ watch(
<TableCreditNote
:grid="pageState.gridView"
:visible-columns="pageState.fieldSelected"
:hide-delete="pageState.currentTab === CreditNoteStatus.Success"
:hide-delete="pageState.currentTab !== CreditNoteStatus.Waiting"
@view="(v) => navigateTo({ statusDialog: 'info', creditId: v.id })"
@delete="(v) => triggerDelete(v.id)"
>