feat: enhance credit note functionality and improve task order handling
This commit is contained in:
parent
91c7db7c7b
commit
dc225ac31b
3 changed files with 125 additions and 36 deletions
|
|
@ -23,7 +23,9 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
taskListGroup?: {
|
taskListGroup?: {
|
||||||
product: RequestWork['productService']['product'];
|
product:
|
||||||
|
| RequestWork['productService']['product']
|
||||||
|
| RequestWork['productService'];
|
||||||
list: (RequestWork & {
|
list: (RequestWork & {
|
||||||
_template?: {
|
_template?: {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -135,6 +137,7 @@ async function getList() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStep(requestWork: RequestWork) {
|
function getStep(requestWork: RequestWork) {
|
||||||
|
if (!requestWork.stepStatus) return 0;
|
||||||
const target = requestWork.stepStatus.find(
|
const target = requestWork.stepStatus.find(
|
||||||
(v) =>
|
(v) =>
|
||||||
v.workStatus === RequestWorkStatus.Ready ||
|
v.workStatus === RequestWorkStatus.Ready ||
|
||||||
|
|
@ -166,7 +169,7 @@ function submit() {
|
||||||
requestWorkStep?: Task;
|
requestWorkStep?: Task;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
selectedEmployee.value.forEach((v, i) => {
|
selectedEmployee.value.forEach((v, i) => {
|
||||||
if (v.stepStatus.length === 0) {
|
if (!v.stepStatus || v.stepStatus.length === 0) {
|
||||||
selected.push({
|
selected.push({
|
||||||
step: 0,
|
step: 0,
|
||||||
requestWorkId: v.id || '',
|
requestWorkId: v.id || '',
|
||||||
|
|
@ -224,7 +227,7 @@ function close() {
|
||||||
|
|
||||||
function onDialogOpen() {
|
function onDialogOpen() {
|
||||||
// assign selected to group
|
// assign selected to group
|
||||||
!props.creditNote && assignTempGroup();
|
assignTempGroup();
|
||||||
|
|
||||||
// match group to check
|
// match group to check
|
||||||
selectedEmployee.value = [];
|
selectedEmployee.value = [];
|
||||||
|
|
@ -232,7 +235,7 @@ function onDialogOpen() {
|
||||||
const matchingItems = tempGroupEdit.value
|
const matchingItems = tempGroupEdit.value
|
||||||
.flatMap((g) => g.list)
|
.flatMap((g) => g.list)
|
||||||
.filter((l) => {
|
.filter((l) => {
|
||||||
if (l.stepStatus.length === 0) {
|
if (!l.stepStatus || l.stepStatus.length === 0) {
|
||||||
return taskList.value.some(
|
return taskList.value.some(
|
||||||
(t) => t.requestWorkStep?.requestWork.id === l.id,
|
(t) => t.requestWorkStep?.requestWork.id === l.id,
|
||||||
);
|
);
|
||||||
|
|
@ -248,8 +251,12 @@ function onDialogOpen() {
|
||||||
function assignTempGroup() {
|
function assignTempGroup() {
|
||||||
if (!props.taskListGroup) return;
|
if (!props.taskListGroup) return;
|
||||||
props.taskListGroup.forEach((newGroup) => {
|
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(
|
const existingGroup = tempGroupEdit.value.find(
|
||||||
(g) => g.product.id === newGroup.product.id,
|
(g) => g.product.id === productId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (existingGroup) {
|
if (existingGroup) {
|
||||||
|
|
@ -260,7 +267,9 @@ function assignTempGroup() {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
tempGroupEdit.value.push({
|
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
|
list: [...newGroup.list], // Ensure a new reference
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,13 @@ import SelectReadyRequestWork from '../09_task-order/SelectReadyRequestWork.vue'
|
||||||
import RefundInformation from './RefundInformation.vue';
|
import RefundInformation from './RefundInformation.vue';
|
||||||
import QuotationFormReceipt from '../05_quotation/QuotationFormReceipt.vue';
|
import QuotationFormReceipt from '../05_quotation/QuotationFormReceipt.vue';
|
||||||
import DialogViewFile from 'src/components/dialog/DialogViewFile.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 { RequestWork } from 'src/stores/request-list/types';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import useOptionStore from 'src/stores/options';
|
import useOptionStore from 'src/stores/options';
|
||||||
|
|
@ -76,15 +82,16 @@ const statusTabForm = ref<
|
||||||
}[]
|
}[]
|
||||||
>([]);
|
>([]);
|
||||||
|
|
||||||
const readonly = computed(
|
// const readonly = computed(
|
||||||
() =>
|
// () =>
|
||||||
creditNoteData.value?.creditNoteStatus === CreditNoteStatus.Pending ||
|
// creditNoteData.value?.creditNoteStatus === CreditNoteStatus.Pending ||
|
||||||
creditNoteData.value?.creditNoteStatus === CreditNoteStatus.Success,
|
// creditNoteData.value?.creditNoteStatus === CreditNoteStatus.Success,
|
||||||
);
|
// );
|
||||||
|
|
||||||
const pageState = reactive({
|
const pageState = reactive({
|
||||||
productDialog: false,
|
productDialog: false,
|
||||||
fileDialog: false,
|
fileDialog: false,
|
||||||
|
mode: 'view' as 'view' | 'edit' | 'info',
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultRemark = '#[quotation-labor]<br/><br/>#[quotation-payment]';
|
const defaultRemark = '#[quotation-labor]<br/><br/>#[quotation-payment]';
|
||||||
|
|
@ -161,9 +168,11 @@ async function initStatus() {
|
||||||
{
|
{
|
||||||
title: 'Pending',
|
title: 'Pending',
|
||||||
status: creditNoteData.value?.id
|
status: creditNoteData.value?.id
|
||||||
? creditNoteData.value.creditNoteStatus === CreditNoteStatus.Success
|
? creditNoteData.value.creditNoteStatus === CreditNoteStatus.Waiting
|
||||||
? 'done'
|
? 'waiting'
|
||||||
: 'doing'
|
: creditNoteData.value.creditNoteStatus === CreditNoteStatus.Success
|
||||||
|
? 'done'
|
||||||
|
: 'doing'
|
||||||
: 'waiting',
|
: 'waiting',
|
||||||
active: () => view.value === CreditNoteStatus.Pending,
|
active: () => view.value === CreditNoteStatus.Pending,
|
||||||
handler: async () => {
|
handler: async () => {
|
||||||
|
|
@ -253,8 +262,9 @@ function assignFormData() {
|
||||||
requestWorkStep: {
|
requestWorkStep: {
|
||||||
requestWork: {
|
requestWork: {
|
||||||
...v,
|
...v,
|
||||||
|
stepStatus: v.stepStatus || [],
|
||||||
request: { ...v.request, quotation: current.quotation },
|
request: { ...v.request, quotation: current.quotation },
|
||||||
},
|
} as RequestWork,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -285,12 +295,24 @@ async function getQuotation() {
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
const payload = formData.value;
|
const payload = formData.value;
|
||||||
|
|
||||||
payload.requestWorkId = formTaskList.value.map((v) => v.requestWorkId);
|
payload.requestWorkId = formTaskList.value.map((v) => v.requestWorkId);
|
||||||
payload.quotationId =
|
payload.quotationId =
|
||||||
typeof route.query['quotationId'] === 'string'
|
(pageState.mode === 'edit'
|
||||||
? route.query['quotationId']
|
? creditNoteData.value?.quotationId
|
||||||
: '';
|
: typeof route.query['quotationId'] === 'string'
|
||||||
const res = await creditNote.createCreditNote(payload);
|
? 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) {
|
if (res) {
|
||||||
await router.push(`/credit-note/${res.id}`);
|
await router.push(`/credit-note/${res.id}`);
|
||||||
|
|
@ -302,6 +324,7 @@ async function submit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
initStatus();
|
initStatus();
|
||||||
|
pageState.mode = 'info';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -526,13 +549,31 @@ function storeDataLocal() {
|
||||||
window.open(url, '_blank');
|
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 () => {
|
onMounted(async () => {
|
||||||
initTheme();
|
initTheme();
|
||||||
initLang();
|
initLang();
|
||||||
await useConfigStore().getConfig();
|
await useConfigStore().getConfig();
|
||||||
await getCreditNote();
|
await getCreditNote();
|
||||||
await getQuotation();
|
await getQuotation();
|
||||||
creditNoteData.value && (await getFileList(creditNoteData.value.id, true));
|
if (creditNoteData.value) {
|
||||||
|
pageState.mode = 'info';
|
||||||
|
await getFileList(creditNoteData.value.id, true);
|
||||||
|
}
|
||||||
initStatus();
|
initStatus();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -587,7 +628,7 @@ onMounted(async () => {
|
||||||
:key="i.title"
|
:key="i.title"
|
||||||
:label="
|
:label="
|
||||||
$t(
|
$t(
|
||||||
`creditNote${i.title === 'title' ? '' : '.stats'}.${i.title}`,
|
`creditNote${i.title === 'title' ? '' : '.status'}.${i.title}`,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
:status-active="i.active?.()"
|
:status-active="i.active?.()"
|
||||||
|
|
@ -616,7 +657,7 @@ onMounted(async () => {
|
||||||
>
|
>
|
||||||
<CreditNoteExpansion
|
<CreditNoteExpansion
|
||||||
v-if="view === null"
|
v-if="view === null"
|
||||||
:readonly="readonly"
|
:readonly="pageState.mode === 'info'"
|
||||||
v-model:reason="formData.reason"
|
v-model:reason="formData.reason"
|
||||||
v-model:detail="formData.detail"
|
v-model:detail="formData.detail"
|
||||||
/>
|
/>
|
||||||
|
|
@ -625,7 +666,7 @@ onMounted(async () => {
|
||||||
<ProductExpansion
|
<ProductExpansion
|
||||||
v-if="view === null"
|
v-if="view === null"
|
||||||
creditNote
|
creditNote
|
||||||
:readonly="readonly"
|
:readonly="pageState.mode === 'info'"
|
||||||
:agentPrice="quotationData?.agentPrice"
|
:agentPrice="quotationData?.agentPrice"
|
||||||
:task-list="taskListGroup"
|
:task-list="taskListGroup"
|
||||||
@add-product="openProductDialog"
|
@add-product="openProductDialog"
|
||||||
|
|
@ -633,7 +674,7 @@ onMounted(async () => {
|
||||||
|
|
||||||
<PaymentExpansion
|
<PaymentExpansion
|
||||||
v-if="view === null"
|
v-if="view === null"
|
||||||
:readonly="readonly"
|
:readonly="pageState.mode === 'info'"
|
||||||
:total-price="summaryPrice.finalPrice"
|
:total-price="summaryPrice.finalPrice"
|
||||||
v-model:payback-type="formData.paybackType"
|
v-model:payback-type="formData.paybackType"
|
||||||
v-model:payback-bank="formData.paybackBank"
|
v-model:payback-bank="formData.paybackBank"
|
||||||
|
|
@ -746,7 +787,7 @@ onMounted(async () => {
|
||||||
v-model:remark="formData.remark"
|
v-model:remark="formData.remark"
|
||||||
:default-remark="defaultRemark"
|
:default-remark="defaultRemark"
|
||||||
:items="[]"
|
:items="[]"
|
||||||
:readonly
|
:readonly="pageState.mode === 'info'"
|
||||||
>
|
>
|
||||||
<template #hint>
|
<template #hint>
|
||||||
{{ $t('general.hintRemark') }}
|
{{ $t('general.hintRemark') }}
|
||||||
|
|
@ -793,7 +834,7 @@ onMounted(async () => {
|
||||||
|
|
||||||
<!-- SEC: footer -->
|
<!-- SEC: footer -->
|
||||||
<footer class="surface-1 q-pa-md full-width">
|
<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 -->
|
<!-- TODO: view example -->
|
||||||
<MainButton
|
<MainButton
|
||||||
class="q-mr-auto"
|
class="q-mr-auto"
|
||||||
|
|
@ -804,16 +845,48 @@ onMounted(async () => {
|
||||||
>
|
>
|
||||||
{{ $t('general.view', { msg: $t('general.example') }) }}
|
{{ $t('general.view', { msg: $t('general.example') }) }}
|
||||||
</MainButton>
|
</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
|
<SaveButton
|
||||||
v-if="!readonly"
|
v-if="
|
||||||
:disabled="taskListGroup.length === 0"
|
!creditNoteData ||
|
||||||
|
creditNoteData?.creditNoteStatus === CreditNoteStatus.Waiting
|
||||||
|
"
|
||||||
|
:disabled="taskListGroup.length === 0 || pageState.mode === 'edit'"
|
||||||
type="submit"
|
type="submit"
|
||||||
@click.stop="(e) => refForm?.submit(e)"
|
@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"
|
icon="mdi-account-multiple-check-outline"
|
||||||
solid
|
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>
|
</nav>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -822,6 +895,7 @@ onMounted(async () => {
|
||||||
<SelectReadyRequestWork
|
<SelectReadyRequestWork
|
||||||
v-if="quotationData"
|
v-if="quotationData"
|
||||||
creditNote
|
creditNote
|
||||||
|
:task-list-group="taskListGroup"
|
||||||
:fetch-params="{ cancelOnly: true, quotationId: quotationData.id }"
|
:fetch-params="{ cancelOnly: true, quotationId: quotationData.id }"
|
||||||
v-model:open="pageState.productDialog"
|
v-model:open="pageState.productDialog"
|
||||||
v-model:task-list="formTaskList"
|
v-model:task-list="formTaskList"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ const { stats, pageMax, page, data, pageSize } = storeToRefs(creditNote);
|
||||||
// NOTE: Variable
|
// NOTE: Variable
|
||||||
const pageState = reactive({
|
const pageState = reactive({
|
||||||
quotationId: '',
|
quotationId: '',
|
||||||
currentTab: CreditNoteStatus.Pending,
|
currentTab: CreditNoteStatus.Waiting,
|
||||||
hideStat: false,
|
hideStat: false,
|
||||||
statusFilter: 'None',
|
statusFilter: 'None',
|
||||||
inputSearch: '',
|
inputSearch: '',
|
||||||
|
|
@ -180,16 +180,22 @@ watch(
|
||||||
<StatCardComponent
|
<StatCardComponent
|
||||||
labelI18n
|
labelI18n
|
||||||
:branch="[
|
: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',
|
icon: 'material-symbols-light:receipt-long',
|
||||||
count: stats[CreditNoteStatus.Pending] || 0,
|
count: stats[CreditNoteStatus.Pending] || 0,
|
||||||
label: `creditNote.stats.${CreditNoteStatus.Pending}`,
|
label: `creditNote.status.${CreditNoteStatus.Pending}`,
|
||||||
color: 'orange',
|
color: 'orange',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'mdi-check-decagram-outline',
|
icon: 'mdi-check-decagram-outline',
|
||||||
count: stats[CreditNoteStatus.Success] || 0,
|
count: stats[CreditNoteStatus.Success] || 0,
|
||||||
label: `creditNote.stats.${CreditNoteStatus.Success}`,
|
label: `creditNote.status.${CreditNoteStatus.Success}`,
|
||||||
color: 'blue',
|
color: 'blue',
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
|
|
@ -345,7 +351,7 @@ watch(
|
||||||
<TableCreditNote
|
<TableCreditNote
|
||||||
:grid="pageState.gridView"
|
:grid="pageState.gridView"
|
||||||
:visible-columns="pageState.fieldSelected"
|
:visible-columns="pageState.fieldSelected"
|
||||||
:hide-delete="pageState.currentTab === CreditNoteStatus.Success"
|
:hide-delete="pageState.currentTab !== CreditNoteStatus.Waiting"
|
||||||
@view="(v) => navigateTo({ statusDialog: 'info', creditId: v.id })"
|
@view="(v) => navigateTo({ statusDialog: 'info', creditId: v.id })"
|
||||||
@delete="(v) => triggerDelete(v.id)"
|
@delete="(v) => triggerDelete(v.id)"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue