feat: credit note (#171)
* feat: add main page credit note * feat: enable credit note route and update menu item states * refactor: add i18n * refactor: edit i18n status * feat: add action column * feat: add empty form page * feat: add get data * feat: add type credit note status * refactor: add type name en * refactor: add type credit note status in type credit note * feat: add hsla colors * refactor: add slot grid * refactor: handle hide kebab edit show only tab tssued * feat: show grid card * feat: i18n * feat: add credit note form and dialog * refactor: add props hide kebab deelete * refactor: hide kebab * style: update color segments to indigo theme * feat: i18n * fix: update labels for credit note fields * refactor: add type * feat: new select quotation * refactor: use new select quotation * feat: navigate to * refactor: function trigger and navigate to * feat: i18n bank * feat: add payment expansion component and integrate into credit note form * refactor: bind i18n pay condition * refactor: navigate to get quotation id * feat: i18n * fix: update label for createdBy field in credit note form * feat: add credit note information expansion component * feat: add Credit Note expansion component and update form layout * refactor: bind quotation id and send * refactor: deelete duplicate type * refactor: show state button * refactor: handle show status * feat: add function update payback status * feat: add return and canceled reasons to credit note translations * feat: enhance SelectReadyRequestWork component with credit note handling and fetch parameters * feat: type * feat: add status handling and optional display for employee table * refactor: rename selectedQuotationId to quotationId in FormCredit component * feat: set default opened state for CreditNoteExpansion and add reason options * feat: update PaymentExpansion to handle payback type selection and clear fields for cash payments * feat: enhance ProductExpansion to support credit note handling and adjust price calculations * feat: implement product handling and price calculation in CreditNote form * feat: add manage attachment function to store * refactor: bind delete credit note * feat: add credit note status and reference fields to types * refactor: update task step handling and simplify request work structure in credit note form * feat: add navigation to quotation from credit note form * feat: enhance upload section layout based on file data * feat: add readonly functionality to credit note form and related components * refactor: remove console log * feat: update i18n * style: add rounded corners to complete view container in quotation form * feat: add RefundInformation component and update credit note form status handling * feat: i18n * feat: update payback status endpoint and add paybackStatus to CreditNote type * feat: enhance QuotationFormReceipt component with optional props and slot support * feat: integrate payback status handling in RefundInformation and FormPage components * feat: add external file group * feat: update API endpoint paths for credit note operations * feat: improve layout and styling in UploadFile components * feat: implement file upload and management in Credit Note * refactor: update upload to check if it is redirect or not * feat: upload file slips * feat: add payback date dispaly * refactor: change module no * fix: icon link to main page instead * feat: add file dialog with image download functionality * fix: view slip * feat: add download button to image viewer * feat: handle after submit * feat: conditionally render bank transfer information * feat: handle upload file on create * feat: handle change payback status * feat: payback type in credit note form * fix: correct reference to quotation data in goToQuotation function --------- Co-authored-by: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Co-authored-by: puriphatt <puriphat@frappet.com> Co-authored-by: Thanaphon Frappet <thanaphon@frappet.com>
This commit is contained in:
parent
0c694dee5d
commit
5e2100eb8d
34 changed files with 2897 additions and 77 deletions
|
|
@ -14,20 +14,25 @@ import FormGroupHead from '../08_request-list/FormGroupHead.vue';
|
|||
import NoData from 'src/components/NoData.vue';
|
||||
|
||||
import { baseUrl } from 'src/stores/utils';
|
||||
import { Task } from 'src/stores/task-order/types';
|
||||
import { Task, TaskStatus } from 'src/stores/task-order/types';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'select', value: RequestWork[]): void;
|
||||
(e: 'afterSubmit'): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
creditNote?: boolean;
|
||||
fetchParams?: Parameters<typeof requestListStore.getRequestWorkList>[0];
|
||||
}>();
|
||||
|
||||
const requestListStore = useRequestList();
|
||||
|
||||
const taskList = defineModel<
|
||||
{
|
||||
step: number;
|
||||
step?: number;
|
||||
requestWorkId: string;
|
||||
requestWorkStep?: Task;
|
||||
requestWorkStep?: Task | { requestWork: RequestWork };
|
||||
}[]
|
||||
>('taskList', {
|
||||
default: [],
|
||||
|
|
@ -36,6 +41,7 @@ const open = defineModel<boolean>('open', { default: false });
|
|||
|
||||
const selectedEmployee = ref<
|
||||
(RequestWork & {
|
||||
taskStatus: TaskStatus;
|
||||
_template?: {
|
||||
id: string;
|
||||
templateName: string;
|
||||
|
|
@ -76,7 +82,7 @@ async function getList() {
|
|||
page: 1,
|
||||
pageSize: 99999,
|
||||
query: state.search,
|
||||
readyToTask: true,
|
||||
...props.fetchParams,
|
||||
});
|
||||
|
||||
if (!res) return;
|
||||
|
|
@ -141,13 +147,19 @@ function submit() {
|
|||
|
||||
selectedEmployee.value.forEach((v, i) => {
|
||||
const curr = v.stepStatus.find(
|
||||
(s) => s.workStatus === RequestWorkStatus.Ready,
|
||||
(s) =>
|
||||
s.workStatus ===
|
||||
(props.creditNote
|
||||
? RequestWorkStatus.Canceled
|
||||
: RequestWorkStatus.Ready),
|
||||
);
|
||||
if (curr) {
|
||||
const task: Task = {
|
||||
...curr,
|
||||
attributes: curr.attributes,
|
||||
workStatus: RequestWorkStatus.Ready,
|
||||
workStatus: props.creditNote
|
||||
? RequestWorkStatus.Canceled
|
||||
: RequestWorkStatus.Ready,
|
||||
taskOrderId: '',
|
||||
requestWork: selectedEmployee.value[i],
|
||||
};
|
||||
|
|
@ -254,7 +266,8 @@ function onDialogOpen() {
|
|||
<div class="q-pa-md full-width">
|
||||
<TableEmployee
|
||||
checkbox-on
|
||||
step-on
|
||||
:step-on="!creditNote"
|
||||
:statusOn="creditNote"
|
||||
:rows="
|
||||
list.map((v) =>
|
||||
Object.assign(v, { _template: getTemplateData(v) }),
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ const props = withDefaults(
|
|||
checkboxOn?: boolean;
|
||||
checkAll?: boolean;
|
||||
stepOn?: boolean;
|
||||
statusOn?: boolean;
|
||||
rows: QTableProps['rows'];
|
||||
grid?: boolean;
|
||||
}>(),
|
||||
|
|
@ -180,7 +181,23 @@ function handleCheck(
|
|||
},
|
||||
...employeeColumn.slice(2),
|
||||
]
|
||||
: employeeColumn
|
||||
: statusOn
|
||||
? [
|
||||
...employeeColumn,
|
||||
{
|
||||
name: 'urgent',
|
||||
align: 'center',
|
||||
label: '',
|
||||
field: (v) => v.product.code,
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
align: 'center',
|
||||
label: 'general.status',
|
||||
field: (v) => v.product.code,
|
||||
},
|
||||
]
|
||||
: employeeColumn
|
||||
"
|
||||
:rows-per-page-options="[0]"
|
||||
:no-data-label="$t('general.noDataTable')"
|
||||
|
|
@ -316,7 +333,7 @@ function handleCheck(
|
|||
<q-th v-for="col in props.cols" :key="col.name" :props="props">
|
||||
{{ col.label && $t(col.label) }}
|
||||
</q-th>
|
||||
<q-th></q-th>
|
||||
<q-th v-if="!statusOn"></q-th>
|
||||
<q-th v-if="$slots.append"></q-th>
|
||||
|
||||
<q-th v-if="$slots.action"></q-th>
|
||||
|
|
@ -338,7 +355,7 @@ function handleCheck(
|
|||
>
|
||||
<q-tr
|
||||
:class="{
|
||||
urgent: props.row.request.quotation.urgent,
|
||||
urgent: props.row.request.quotation?.urgent,
|
||||
dark: $q.dark.isActive,
|
||||
['disabled-row']:
|
||||
selectedEmployee.length > 0 &&
|
||||
|
|
@ -392,7 +409,7 @@ function handleCheck(
|
|||
<q-img
|
||||
class="text-center"
|
||||
:ratio="1"
|
||||
:src="`${baseUrl}/employee/${props.row.request.employee.id}/image/${props.row.request.employee.selectedImage}`"
|
||||
:src="`${baseUrl}/employee/${props.row.request.employee?.id}/image/${props.row.request.employee?.selectedImage}`"
|
||||
>
|
||||
<template #error>
|
||||
<span class="full-width full-height">
|
||||
|
|
@ -407,27 +424,27 @@ function handleCheck(
|
|||
</div>
|
||||
|
||||
<div class="app-text-muted">
|
||||
{{ props.row.request.employee.code }}
|
||||
{{ props.row.request.employee?.code }}
|
||||
</div>
|
||||
</div>
|
||||
<Icon
|
||||
class="q-ml-md"
|
||||
:class="`app-text-${props.row.request.employee.gender}`"
|
||||
:icon="`material-symbols:${props.row.request.employee.gender}`"
|
||||
:class="`app-text-${props.row.request.employee?.gender}`"
|
||||
:icon="`material-symbols:${props.row.request.employee?.gender}`"
|
||||
width="24px"
|
||||
/>
|
||||
</div>
|
||||
</q-td>
|
||||
<q-td>{{ calculateAge(props.row.request.employee.dateOfBirth) }}</q-td>
|
||||
<q-td>{{ calculateAge(props.row.request.employee?.dateOfBirth) }}</q-td>
|
||||
<q-td>
|
||||
{{
|
||||
useOptionStore().mapOption(props.row.request.employee.nationality)
|
||||
useOptionStore().mapOption(props.row.request.employee?.nationality)
|
||||
}}
|
||||
</q-td>
|
||||
<q-td>
|
||||
{{
|
||||
dateFormatJS({
|
||||
date: props.row.request.quotation.dueDate,
|
||||
date: props.row.request.quotation?.dueDate,
|
||||
locale: $i18n.locale,
|
||||
dayStyle: '2-digit',
|
||||
monthStyle: 'short',
|
||||
|
|
@ -436,7 +453,7 @@ function handleCheck(
|
|||
</q-td>
|
||||
<q-td>
|
||||
<ExpirationDate
|
||||
:expiration-date="new Date(props.row.request.quotation.dueDate)"
|
||||
:expiration-date="new Date(props.row.request.quotation?.dueDate)"
|
||||
/>
|
||||
</q-td>
|
||||
<q-td>
|
||||
|
|
@ -444,12 +461,12 @@ function handleCheck(
|
|||
class="cursor-pointer link"
|
||||
@click="goToQuotation(props.row.request.quotation)"
|
||||
>
|
||||
{{ props.row.request.quotation.code }}
|
||||
{{ props.row.request.quotation?.code }}
|
||||
</span>
|
||||
</q-td>
|
||||
<q-td>
|
||||
<BadgeComponent
|
||||
v-if="props.row.request.quotation.urgent"
|
||||
v-if="props.row.request.quotation?.urgent"
|
||||
icon="mdi-fire"
|
||||
:title="$t('general.urgent2')"
|
||||
hsla-color="--gray-1-hsl"
|
||||
|
|
@ -457,6 +474,12 @@ function handleCheck(
|
|||
solid
|
||||
/>
|
||||
</q-td>
|
||||
<q-td v-if="statusOn">
|
||||
<BadgeComponent
|
||||
:title="$t('creditNote.status.Canceled')"
|
||||
hsla-color="--red-8-hsl"
|
||||
/>
|
||||
</q-td>
|
||||
<q-td v-if="$slots.append">
|
||||
<slot name="append" :props="props"></slot>
|
||||
</q-td>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ const fileData = defineModel<
|
|||
loaded: number;
|
||||
total: number;
|
||||
url?: string;
|
||||
placeholder?: boolean;
|
||||
}[]
|
||||
>('fileData', { default: [] });
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -26,12 +26,14 @@ const taskProduct = defineModel<{ productId: string; discount?: number }[]>(
|
|||
},
|
||||
);
|
||||
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
readonly?: boolean;
|
||||
agentPrice?: boolean;
|
||||
taskList: {
|
||||
product: RequestWork['productService']['product'];
|
||||
list: RequestWork[];
|
||||
}[];
|
||||
creditNote?: boolean;
|
||||
}>();
|
||||
|
||||
defineEmits<{
|
||||
|
|
@ -55,15 +57,28 @@ function openList(index: number) {
|
|||
|
||||
function calcPricePerUnit(product: RequestWork['productService']['product']) {
|
||||
return product.vatIncluded
|
||||
? product.serviceCharge / (1 + (config.value?.vat || 0.07))
|
||||
: product.serviceCharge;
|
||||
? (props.creditNote
|
||||
? props.agentPrice
|
||||
? product.agentPrice
|
||||
: product.price
|
||||
: product.serviceCharge) /
|
||||
(1 + (config.value?.vat || 0.07))
|
||||
: props.creditNote
|
||||
? props.agentPrice
|
||||
? product.agentPrice
|
||||
: product.price
|
||||
: product.serviceCharge;
|
||||
}
|
||||
|
||||
function calcPrice(
|
||||
product: RequestWork['productService']['product'],
|
||||
amount: number,
|
||||
) {
|
||||
const pricePerUnit = product.serviceCharge;
|
||||
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.vatIncluded
|
||||
|
|
@ -102,7 +117,16 @@ function calcPrice(
|
|||
|
||||
<main class="q-px-md q-py-sm surface-1">
|
||||
<q-table
|
||||
:columns="productColumn"
|
||||
:columns="
|
||||
creditNote
|
||||
? productColumn.filter(
|
||||
(v) =>
|
||||
v.name !== 'discount' &&
|
||||
v.name !== 'priceBeforeVat' &&
|
||||
v.name !== 'vat',
|
||||
)
|
||||
: productColumn
|
||||
"
|
||||
:rows="taskList"
|
||||
bordered
|
||||
flat
|
||||
|
|
@ -174,7 +198,7 @@ function calcPrice(
|
|||
}}
|
||||
</q-td>
|
||||
<!-- TODO: display price detail -->
|
||||
<q-td align="center">
|
||||
<q-td align="center" v-if="!creditNote">
|
||||
<q-input
|
||||
:readonly
|
||||
:bg-color="readonly ? 'transparent' : ''"
|
||||
|
|
@ -213,11 +237,11 @@ function calcPrice(
|
|||
/>
|
||||
</q-td>
|
||||
<!-- before vat -->
|
||||
<q-td class="text-right">
|
||||
<q-td class="text-right" v-if="!creditNote">
|
||||
{{ formatNumberDecimal(calcPricePerUnit(props.row.product), 2) }}
|
||||
</q-td>
|
||||
<!-- vat -->
|
||||
<q-td class="text-right">
|
||||
<q-td class="text-right" v-if="!creditNote">
|
||||
{{
|
||||
formatNumberDecimal(
|
||||
props.row.product.calcVat
|
||||
|
|
@ -267,7 +291,11 @@ function calcPrice(
|
|||
|
||||
<q-tr v-show="currentBtnOpen[props.rowIndex]" :props="props">
|
||||
<q-td colspan="100%" style="padding: 16px">
|
||||
<TableEmployee step-on :rows="props.row.list" />
|
||||
<TableEmployee
|
||||
:step-on="!creditNote"
|
||||
:status-on="creditNote"
|
||||
:rows="props.row.list"
|
||||
/>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1130,6 +1130,7 @@ watch([currentFormData.value.taskStatus], () => {
|
|||
|
||||
<!-- SEC: Dialog -->
|
||||
<SelectReadyRequestWork
|
||||
:fetch-params="{ readyToTask: true }"
|
||||
v-model:open="pageState.productDialog"
|
||||
v-model:task-list="currentFormData.taskList"
|
||||
@after-submit="
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue