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
212
src/components/shared/select/SelectQuotation.vue
Normal file
212
src/components/shared/select/SelectQuotation.vue
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
import { createSelect, SelectProps } from './select';
|
||||
import SelectInput from '../SelectInput.vue';
|
||||
|
||||
import useOptionStore from 'src/stores/options';
|
||||
|
||||
import { Quotation, QuotationFull } from 'src/stores/quotations/types';
|
||||
|
||||
import { useQuotationStore as useStore } from 'src/stores/quotations';
|
||||
|
||||
type SelectOption = Quotation | QuotationFull;
|
||||
|
||||
const value = defineModel<string | null | undefined>('value', {
|
||||
required: true,
|
||||
});
|
||||
const valueOption = defineModel<SelectOption>('valueOption', {
|
||||
required: false,
|
||||
});
|
||||
|
||||
const selectOptions = ref<SelectOption[]>([]);
|
||||
|
||||
const { getQuotationList: getList, getQuotation: getById } = useStore();
|
||||
|
||||
defineEmits<{
|
||||
(e: 'create'): void;
|
||||
}>();
|
||||
|
||||
type ExclusiveProps = {
|
||||
codeOnly?: boolean;
|
||||
selectFirstValue?: boolean;
|
||||
branchVirtual?: boolean;
|
||||
checkRole?: string[];
|
||||
};
|
||||
|
||||
const props = defineProps<SelectProps<typeof getList> & ExclusiveProps>();
|
||||
|
||||
const { getOptions, setFirstValue, getSelectedOption, filter } =
|
||||
createSelect<SelectOption>(
|
||||
{
|
||||
value,
|
||||
valueOption,
|
||||
selectOptions,
|
||||
getList: async (query) => {
|
||||
const ret = await getList({
|
||||
query,
|
||||
...props.params,
|
||||
pageSize: 30,
|
||||
hasCancel: true,
|
||||
includeRegisteredBranch: true,
|
||||
});
|
||||
if (ret) return ret.result;
|
||||
},
|
||||
getByValue: async (id) => {
|
||||
const ret = await getById(id);
|
||||
if (ret) return ret;
|
||||
},
|
||||
},
|
||||
{ valueField: 'id' },
|
||||
);
|
||||
|
||||
function getCustomerName(
|
||||
record: Quotation,
|
||||
opts?: {
|
||||
locale?: string;
|
||||
noCode?: boolean;
|
||||
},
|
||||
) {
|
||||
const customer = record.customerBranch;
|
||||
|
||||
return (
|
||||
{
|
||||
['CORP']: {
|
||||
['eng']: customer.registerNameEN,
|
||||
['tha']: customer.registerName,
|
||||
}[opts?.locale || 'eng'],
|
||||
['PERS']:
|
||||
{
|
||||
['eng']: `${useOptionStore().mapOption(customer?.namePrefix || '')} ${customer?.firstNameEN} ${customer?.lastNameEN}`,
|
||||
['tha']: `${useOptionStore().mapOption(customer?.namePrefix || '')} ${customer?.firstName} ${customer?.lastName}`,
|
||||
}[opts?.locale || 'eng'] || '-',
|
||||
}[customer.customer.customerType] +
|
||||
(opts?.noCode ? '' : ' ' + `(${customer.code})`)
|
||||
);
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await getOptions();
|
||||
|
||||
if (props.autoSelectOnSingle && selectOptions.value.length === 1) {
|
||||
setFirstValue();
|
||||
}
|
||||
|
||||
if (props.selectFirstValue) {
|
||||
setDefaultValue();
|
||||
} else await getSelectedOption();
|
||||
});
|
||||
|
||||
function setDefaultValue() {
|
||||
setFirstValue();
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<SelectInput
|
||||
v-model="value"
|
||||
option-value="id"
|
||||
incremental
|
||||
:label
|
||||
:placeholder
|
||||
:readonly
|
||||
:disable="disabled"
|
||||
:option="selectOptions"
|
||||
:hide-selected="false"
|
||||
:fill-input="false"
|
||||
:rules="[
|
||||
(v: string) => !props.required || !!v || $t('form.error.required'),
|
||||
]"
|
||||
@filter="filter"
|
||||
>
|
||||
<template #append v-if="clearable">
|
||||
<q-icon
|
||||
v-if="!readonly && value"
|
||||
name="mdi-close-circle"
|
||||
@click.stop="value = ''"
|
||||
class="cursor-pointer clear-btn"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #option="{ scope }">
|
||||
<q-item
|
||||
v-if="scope.opt"
|
||||
v-bind="scope.itemProps"
|
||||
class="row items-start col-12 no-padding"
|
||||
>
|
||||
<div class="q-mx-sm q-my-xs">
|
||||
<q-icon name="mdi-file" style="color: var(--brand-1)" />
|
||||
</div>
|
||||
|
||||
<div class="q-pt-xs">
|
||||
<span class="row">
|
||||
<span style="font-weight: 600">
|
||||
{{ $t('productService.service.work') }}:
|
||||
</span>
|
||||
|
||||
{{ scope.opt.workName }}
|
||||
({{ scope.opt.code }})
|
||||
|
||||
<q-badge
|
||||
dense
|
||||
class="surface-3 q-ml-sm"
|
||||
rounded
|
||||
style="color: var(--foreground)"
|
||||
>
|
||||
{{ scope.opt._count.canceledWork || 0 }}
|
||||
</q-badge>
|
||||
</span>
|
||||
|
||||
<div class="text-caption app-text-muted-2 q-mb-xs">
|
||||
<span class="col column">
|
||||
<!-- TODO: register branch id -->
|
||||
{{ $t(`creditNote.label.servicePoint`) }}
|
||||
{{
|
||||
$i18n.locale === 'eng'
|
||||
? scope.opt.registeredBranch.nameEN
|
||||
: scope.opt.registeredBranch.name
|
||||
}}
|
||||
({{ scope.opt.registeredBranch.code }})
|
||||
</span>
|
||||
<span class="col">
|
||||
{{ $t('quotation.customer') }}
|
||||
{{ getCustomerName(scope.opt, { locale: $i18n.locale }) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</q-item>
|
||||
<q-separator class="q-mx-sm" />
|
||||
</template>
|
||||
|
||||
<template #selected-item="{ scope }">
|
||||
<div v-if="scope.opt" class="row items-center no-wrap">
|
||||
<div class="q-mr-sm">
|
||||
<span style="font-weight: 600">
|
||||
{{ $t('productService.service.work') }}:
|
||||
</span>
|
||||
|
||||
{{ scope.opt.workName }}
|
||||
({{ scope.opt.code }})
|
||||
<q-badge
|
||||
dense
|
||||
class="surface-3 q-ml-xs"
|
||||
rounded
|
||||
style="color: var(--foreground)"
|
||||
>
|
||||
{{ scope.opt._count.canceledWork || 0 }}
|
||||
</q-badge>
|
||||
</div>
|
||||
<div class="text-caption app-text-muted-2">
|
||||
{{ $t(`creditNote.label.servicePoint`) }}
|
||||
{{
|
||||
$i18n.locale === 'eng'
|
||||
? scope.opt.registeredBranch.nameEN
|
||||
: scope.opt.registeredBranch.name
|
||||
}}
|
||||
({{ scope.opt.registeredBranch.code }}),
|
||||
{{ $t('quotation.customer') }}
|
||||
{{ getCustomerName(scope.opt, { locale: $i18n.locale }) }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</SelectInput>
|
||||
</template>
|
||||
Loading…
Add table
Add a link
Reference in a new issue