feat: debit note (#172)
* feat: new file * feat: function api debit * feat: add route debit * feat: new form page * refactor: show menu debit * refactor: add type debit note status * feat: add i18n * feat: add constants * feat: add stores * feat: layout * feat: add function * refactor: change name value * feat: form select quotation * refactor: change name url * refactor: use form debit * refactor: change src import * refactor: move file form debit * refactor: add i18n * feat: add type debit note * refactor: add columns * refactor: bind value columns * refactor: change name Table * refactor: edit type * refactor: bind type debit note * refactor: bind value debit * refactor: chame name function * fix: calculate page * refactor: delete table * refactor: change name get list * refactor: change i18n * refactor: change name value * refactor: bind navigate and trigger delete * refactor: format number deciml * refactor: add i18n * feat: new page * refactor: add color debit * feat: Debit tab #178 * feat: TableRequest * refactor: edit type pay condition * refactor: add i18n btn submit * refactor: use type enum * feat: edit layout product expansion * refactor: bind function * refactor: show code * feat: add input search and select status * feat: paymentform * refactor: edit type * refactor: add manage file and edit end point * feat: add form.ts * refactor: send mode * refactor: edit v-model of due date * feat: submit create debit * fix: status * refactor: handle data not allow * fix: call updateDebitNote in edit mode and simplify payload handling * refactor: hide edit * refactor: handle pay condition only full * refactor: delete pay split * refactor: add query * refactor: handle is debit note * refactor: handle is quotation * refactor: add props hide * refactor: tap payment and receipt * refactor: add i18n * feat: view document * refactor: handle btn view doc * refactor: use my remark --------- Co-authored-by: Thanaphon Frappet <thanaphon@frappet.com> Co-authored-by: nwpptrs <jay02499@gmail.com> Co-authored-by: aif912752 <siripak@chamomind.com>
This commit is contained in:
parent
e3c781f857
commit
79240f53b0
32 changed files with 4172 additions and 12 deletions
51
src/pages/12_debit-note/expansion/DebitNoteExpansion.vue
Normal file
51
src/pages/12_debit-note/expansion/DebitNoteExpansion.vue
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<script setup lang="ts">
|
||||
import SelectInput from 'src/components/shared/SelectInput.vue';
|
||||
|
||||
defineProps<{
|
||||
readonly?: boolean;
|
||||
}>();
|
||||
|
||||
const reason = defineModel<string>('reason');
|
||||
const detail = defineModel<string>('detail');
|
||||
</script>
|
||||
<template>
|
||||
<q-expansion-item
|
||||
dense
|
||||
:default-opened="true"
|
||||
class="overflow-hidden bordered full-width"
|
||||
switch-toggle-side
|
||||
style="border-radius: var(--radius-2)"
|
||||
expand-icon="mdi-chevron-down-circle"
|
||||
header-class="surface-1 q-py-sm text-medium text-body1"
|
||||
>
|
||||
<template #header>
|
||||
<span>
|
||||
{{ $t('debitNote.label.debitNoteInformation') }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<main class="q-px-md q-py-sm surface-1 row q-col-gutter-sm">
|
||||
<SelectInput
|
||||
:readonly
|
||||
for="select-debit-note-specify-reason"
|
||||
:label="$t('debitNote.label.specifyReasonForDebit')"
|
||||
class="col-md col-12"
|
||||
v-model="reason"
|
||||
:option="[
|
||||
{ label: $t('debitNote.label.reasonReturn'), value: 'Return' },
|
||||
{ label: $t('debitNote.label.reasonCanceled'), value: 'Canceled' },
|
||||
]"
|
||||
></SelectInput>
|
||||
<q-input
|
||||
:readonly
|
||||
for="input-debit-note-additional-detail"
|
||||
:label="$t('debitNote.label.additionalDetail')"
|
||||
outlined
|
||||
dense
|
||||
class="col"
|
||||
v-model="detail"
|
||||
></q-input>
|
||||
</main>
|
||||
</q-expansion-item>
|
||||
</template>
|
||||
<style scoped></style>
|
||||
120
src/pages/12_debit-note/expansion/DocumentExpansion.vue
Normal file
120
src/pages/12_debit-note/expansion/DocumentExpansion.vue
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
<script lang="ts" setup>
|
||||
import SelectBranch from 'src/components/shared/select/SelectBranch.vue';
|
||||
import SelectCustomer from 'src/components/shared/select/SelectCustomer.vue';
|
||||
import DatePicker from 'src/components/shared/DatePicker.vue';
|
||||
import DataDisplay from 'src/components/08_request-list/DataDisplay.vue';
|
||||
|
||||
defineProps<{
|
||||
readonly?: boolean;
|
||||
}>();
|
||||
|
||||
defineEmits<{
|
||||
(e: 'gotoQuotation'): void;
|
||||
}>();
|
||||
|
||||
const registeredBranchId = defineModel<string>('registeredBranchId');
|
||||
const customerId = defineModel<string>('customerId');
|
||||
const issueDate = defineModel<string>('issueDate');
|
||||
const dueDate = defineModel<string | Date>('dueDate');
|
||||
|
||||
const quotationCode = defineModel<string>('quotationCode');
|
||||
const quotationWorkName = defineModel<string>('quotationWorkName');
|
||||
const quotationContactName = defineModel<string>('quotationContactName');
|
||||
const quotationContactTel = defineModel<string>('quotationContactTel');
|
||||
const quotationCreatedBy = defineModel<string>('quotationCreatedBy');
|
||||
</script>
|
||||
<template>
|
||||
<q-expansion-item
|
||||
default-opened
|
||||
dense
|
||||
class="overflow-hidden bordered full-width"
|
||||
switch-toggle-side
|
||||
style="border-radius: var(--radius-2)"
|
||||
expand-icon="mdi-chevron-down-circle"
|
||||
header-class="surface-1 q-py-sm text-medium text-body1"
|
||||
>
|
||||
<template #header>
|
||||
<span>
|
||||
{{ $t('general.information', { msg: $t('general.document') }) }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<main class="q-px-md q-py-sm surface-1 row q-col-gutter-sm">
|
||||
<SelectBranch
|
||||
readonly
|
||||
class="col-md-4 col-12"
|
||||
:label="`${$t('creditNote.label.quotationRegisteredBranch')}`"
|
||||
v-model:value="registeredBranchId"
|
||||
/>
|
||||
<SelectCustomer
|
||||
readonly
|
||||
simple
|
||||
class="col-md-4 col-12"
|
||||
:label="`${$t('creditNote.label.customer')}`"
|
||||
v-model:value="customerId"
|
||||
/>
|
||||
<DatePicker
|
||||
:label="$t('general.createdAt')"
|
||||
class="col-md-2 col-6"
|
||||
:model-value="issueDate || new Date(Date.now())"
|
||||
:readonly
|
||||
:disabled="!readonly"
|
||||
/>
|
||||
<DatePicker
|
||||
:label="$t('general.createdAt')"
|
||||
class="col-md-2 col-6"
|
||||
:model-value="dueDate || new Date(Date.now())"
|
||||
@update:model-value="
|
||||
(v) => {
|
||||
if (typeof v === 'string') dueDate = v;
|
||||
}
|
||||
"
|
||||
:readonly
|
||||
/>
|
||||
|
||||
<DataDisplay
|
||||
clickable
|
||||
class="col-md col-6"
|
||||
style="padding-inline: 20px"
|
||||
:label="$t('creditNote.label.quotationCode')"
|
||||
:value="quotationCode"
|
||||
@label-click="$emit('gotoQuotation')"
|
||||
/>
|
||||
|
||||
<q-input
|
||||
readonly
|
||||
:label="$t('creditNote.label.quotationWorkName')"
|
||||
outlined
|
||||
dense
|
||||
class="col-md col-6"
|
||||
v-model="quotationWorkName"
|
||||
/>
|
||||
<q-input
|
||||
readonly
|
||||
:label="$t('quotation.contactName')"
|
||||
outlined
|
||||
dense
|
||||
class="col-md col-6"
|
||||
v-model="quotationContactName"
|
||||
/>
|
||||
<q-input
|
||||
readonly
|
||||
:label="$t('general.telephone')"
|
||||
outlined
|
||||
dense
|
||||
class="col-md col-6"
|
||||
v-model="quotationContactTel"
|
||||
/>
|
||||
<q-input
|
||||
:readonly
|
||||
:label="$t('creditNote.label.quotationCreatedBy')"
|
||||
outlined
|
||||
dense
|
||||
class="col-md col-6"
|
||||
:disable="!readonly"
|
||||
:model-value="quotationCreatedBy || '-'"
|
||||
/>
|
||||
</main>
|
||||
</q-expansion-item>
|
||||
</template>
|
||||
<style scoped></style>
|
||||
116
src/pages/12_debit-note/expansion/PaymentExpansion.vue
Normal file
116
src/pages/12_debit-note/expansion/PaymentExpansion.vue
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
<script lang="ts" setup>
|
||||
import useOptionStore from 'src/stores/options';
|
||||
import { watch } from 'vue';
|
||||
import QuotationFormInfo from 'src/pages/05_quotation/QuotationFormInfo.vue';
|
||||
import { PayCondition } from 'src/stores/quotations';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
readonly?: boolean;
|
||||
installmentNo?: number[];
|
||||
installmentAmount?: number;
|
||||
}>(),
|
||||
{},
|
||||
);
|
||||
|
||||
const payBillDate = defineModel<Date | null | undefined>('payBillDate', {
|
||||
required: false,
|
||||
});
|
||||
const payType = defineModel<PayCondition>('payType', { required: true });
|
||||
const paySplitCount = defineModel<number | null>('paySplitCount', {
|
||||
default: 1,
|
||||
});
|
||||
const paySplit = defineModel<{ no: number; name?: string; amount: number }[]>(
|
||||
'paySplit',
|
||||
{ required: true },
|
||||
);
|
||||
const summaryPrice = defineModel<{
|
||||
totalPrice: number;
|
||||
totalDiscount: number;
|
||||
vat: number;
|
||||
vatExcluded: number;
|
||||
finalPrice: number;
|
||||
}>('summaryPrice', {
|
||||
required: true,
|
||||
default: {
|
||||
totalPrice: 0,
|
||||
totalDiscount: 0,
|
||||
vat: 0,
|
||||
vatExcluded: 0,
|
||||
finalPrice: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const optionStore = useOptionStore();
|
||||
</script>
|
||||
<template>
|
||||
<q-expansion-item
|
||||
dense
|
||||
class="overflow-hidden bordered full-width"
|
||||
switch-toggle-side
|
||||
style="border-radius: var(--radius-2)"
|
||||
expand-icon="mdi-chevron-down-circle"
|
||||
header-class="surface-1 q-py-sm text-medium text-body1"
|
||||
>
|
||||
<template #header>
|
||||
<span>
|
||||
{{ $t('general.payment') }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<main class="surface-1 q-pa-md full-width">
|
||||
<QuotationFormInfo
|
||||
v-bind="{ ...$props }"
|
||||
debit-note
|
||||
v-model:pay-type="payType"
|
||||
v-model:pay-split-count="paySplitCount"
|
||||
v-model:pay-split="paySplit"
|
||||
v-model:pay-bill-date="payBillDate"
|
||||
v-model:summary-price="summaryPrice"
|
||||
/>
|
||||
</main>
|
||||
</q-expansion-item>
|
||||
</template>
|
||||
<style scoped>
|
||||
.icon-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
aspect-ratio: 1/1;
|
||||
font-size: 1.5rem;
|
||||
padding: var(--size-1);
|
||||
border-radius: var(--radius-2);
|
||||
}
|
||||
|
||||
:deep(.price-tag .q-field__control) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 90px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.credit-note-color {
|
||||
--_color: var(--indigo-10-hsl);
|
||||
}
|
||||
|
||||
.bg-color {
|
||||
color: white;
|
||||
background: hsla(var(--_color));
|
||||
}
|
||||
|
||||
.dark .bg-color {
|
||||
--_color: var(--orange-6-hsl);
|
||||
}
|
||||
|
||||
.bg-color-light {
|
||||
background: hsla(var(--_color) / 0.1);
|
||||
}
|
||||
|
||||
.dark .bg-color-light {
|
||||
--_color: var(--orange-6-hsl / 0.2);
|
||||
}
|
||||
|
||||
.price-container > * {
|
||||
padding: var(--size-1);
|
||||
}
|
||||
</style>
|
||||
105
src/pages/12_debit-note/expansion/ProductExpansion.vue
Normal file
105
src/pages/12_debit-note/expansion/ProductExpansion.vue
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<script lang="ts" setup>
|
||||
import ProductItem from 'src/components/05_quotation/ProductItem.vue';
|
||||
import { AddButton } from 'components/button';
|
||||
import {
|
||||
ProductRelation,
|
||||
ProductServiceList,
|
||||
QuotationPayload,
|
||||
} from 'src/stores/quotations';
|
||||
|
||||
defineProps<{
|
||||
readonly?: boolean;
|
||||
agentPrice: boolean;
|
||||
installmentInput?: boolean;
|
||||
maxInstallment?: number | null;
|
||||
creditNote?: boolean;
|
||||
employeeRows: {
|
||||
foreignRefNo: string;
|
||||
employeeName: string;
|
||||
birthDate: string;
|
||||
gender: string;
|
||||
age: string;
|
||||
nationality: string;
|
||||
documentExpireDate: string;
|
||||
imgUrl: string;
|
||||
status: string;
|
||||
}[];
|
||||
}>();
|
||||
|
||||
const rows = defineModel<
|
||||
Required<QuotationPayload['productServiceList'][number]>[]
|
||||
>('rows', { required: true });
|
||||
|
||||
defineEmits<{
|
||||
(e: 'addProduct'): void;
|
||||
(e: 'viewFile', data: ProductRelation): void;
|
||||
(e: 'delete', index: number): void;
|
||||
(
|
||||
e: 'updateTable',
|
||||
v: QuotationPayload['productServiceList'][number],
|
||||
opt?: {
|
||||
newInstallmentNo: number;
|
||||
},
|
||||
): void;
|
||||
|
||||
(e: 'updateRows', v: Required<ProductServiceList>[]): void;
|
||||
}>();
|
||||
</script>
|
||||
<template>
|
||||
<q-expansion-item
|
||||
dense
|
||||
class="overflow-hidden bordered full-width"
|
||||
switch-toggle-side
|
||||
style="border-radius: var(--radius-2)"
|
||||
expand-icon="mdi-chevron-down-circle"
|
||||
header-class="surface-1 q-py-sm text-medium text-body1"
|
||||
default-opened
|
||||
>
|
||||
<template #header>
|
||||
<span
|
||||
class="row items-center justify-between full-width"
|
||||
style="min-height: 31.01px"
|
||||
>
|
||||
{{ $t('general.information', { msg: $t('taskOrder.productList') }) }}
|
||||
<AddButton
|
||||
icon-only
|
||||
@click.stop="$emit('addProduct')"
|
||||
v-if="!readonly"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<main class="q-px-md q-py-sm surface-1">
|
||||
<ProductItem
|
||||
:installment-input
|
||||
:max-installment
|
||||
:readonly
|
||||
:agent-price
|
||||
:employee-rows
|
||||
:rows="rows"
|
||||
@delete="(v) => $emit('delete', v)"
|
||||
@update:rows="(v) => $emit('updateRows', v)"
|
||||
@update-table="(data, opt) => $emit('updateTable', data, opt)"
|
||||
@view-file="(v) => $emit('viewFile', v)"
|
||||
/>
|
||||
</main>
|
||||
</q-expansion-item>
|
||||
</template>
|
||||
<style scoped>
|
||||
.product-status {
|
||||
padding-left: 8px;
|
||||
border-radius: 20px;
|
||||
color: hsl(var(--_color));
|
||||
background: hsla(var(--_color) / 0.15);
|
||||
|
||||
&.warning {
|
||||
--_color: var(--warning-bg);
|
||||
}
|
||||
&.positive {
|
||||
--_color: var(--positive-bg);
|
||||
}
|
||||
&.negative {
|
||||
--_color: var(--negative-bg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
67
src/pages/12_debit-note/expansion/RemarkExpansion.vue
Normal file
67
src/pages/12_debit-note/expansion/RemarkExpansion.vue
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
<script lang="ts" setup>
|
||||
defineProps<{
|
||||
readonly?: boolean;
|
||||
}>();
|
||||
|
||||
const remark = defineModel<string>('remark', { default: '' });
|
||||
</script>
|
||||
<template>
|
||||
<q-expansion-item
|
||||
dense
|
||||
class="overflow-hidden bordered full-width"
|
||||
switch-toggle-side
|
||||
style="border-radius: var(--radius-2)"
|
||||
expand-icon="mdi-chevron-down-circle"
|
||||
header-class="surface-1 q-py-sm text-medium text-body1"
|
||||
>
|
||||
<template #header>
|
||||
<span>
|
||||
{{ $t('general.remark') }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<main class="surface-1 q-pa-md full-width">
|
||||
<q-editor
|
||||
dense
|
||||
:readonly="readonly"
|
||||
:model-value="remark"
|
||||
min-height="5rem"
|
||||
class="full-width"
|
||||
toolbar-bg="input-border"
|
||||
style="cursor: auto; color: var(--foreground)"
|
||||
:content-class="readonly ? 'q-mt-sm' : 'bordered q-mt-sm rounded'"
|
||||
:flat="!readonly"
|
||||
:style="`width: ${$q.screen.gt.xs ? '100%' : '63vw'}`"
|
||||
:toolbar="[['left', 'center', 'justify'], ['clip']]"
|
||||
:toolbar-toggle-color="readonly ? 'disabled' : 'primary'"
|
||||
:toolbar-color="readonly ? 'disabled' : $q.dark.isActive ? 'white' : ''"
|
||||
:definitions="{
|
||||
clip: {
|
||||
icon: 'mdi-paperclip',
|
||||
tip: 'Upload',
|
||||
disable: readonly,
|
||||
handler: () => console.log('upload'),
|
||||
},
|
||||
}"
|
||||
@update:model-value="
|
||||
(v) => {
|
||||
remark = v;
|
||||
}
|
||||
"
|
||||
/>
|
||||
</main>
|
||||
</q-expansion-item>
|
||||
</template>
|
||||
<style scoped>
|
||||
:deep(.q-editor__toolbar-group):nth-child(2) {
|
||||
margin-left: auto !important;
|
||||
}
|
||||
|
||||
:deep(.q-editor__toolbar.row.no-wrap.scroll-x) {
|
||||
background-color: var(--surface-2) !important;
|
||||
}
|
||||
|
||||
:deep(.q-editor__toolbar) {
|
||||
border-color: var(--surface-3) !important;
|
||||
}
|
||||
</style>
|
||||
78
src/pages/12_debit-note/expansion/WorkerItemExpansion.vue
Normal file
78
src/pages/12_debit-note/expansion/WorkerItemExpansion.vue
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<script setup lang="ts">
|
||||
import { AddButton } from 'components/button';
|
||||
import WorkerItem from 'src/components/05_quotation/WorkerItem.vue';
|
||||
|
||||
defineProps<{
|
||||
readonly?: boolean;
|
||||
hideBtnAddWorker?: boolean;
|
||||
employeeAmount?: number;
|
||||
rowWorker: {
|
||||
foreignRefNo: string;
|
||||
employeeName: string;
|
||||
birthDate: string;
|
||||
gender: string;
|
||||
age: string;
|
||||
nationality: string;
|
||||
documentExpireDate: string;
|
||||
imgUrl?: string;
|
||||
status: string;
|
||||
}[];
|
||||
}>();
|
||||
|
||||
defineEmits<{
|
||||
(e: 'addWorker'): void;
|
||||
(e: 'edit'): void;
|
||||
(e: 'update:employeeAmount', number: number): void;
|
||||
(e: 'delete', index: number): void;
|
||||
}>();
|
||||
|
||||
const toggleWorker = defineModel<boolean>('toggleWorker');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-expansion-item
|
||||
for="item-up"
|
||||
id="item-up"
|
||||
dense
|
||||
class="overflow-hidden"
|
||||
switch-toggle-side
|
||||
default-opened
|
||||
style="border-radius: var(--radius-2)"
|
||||
expand-icon="mdi-chevron-down-circle"
|
||||
header-class="surface-1"
|
||||
>
|
||||
<template v-slot:header>
|
||||
<section class="row items-center full-width">
|
||||
<div class="row items-center q-pr-md q-py-sm">
|
||||
<span class="q-mr-md" style="font-size: 18px">
|
||||
{{ $t('quotation.employeeList') }}
|
||||
</span>
|
||||
<template v-if="!readonly">
|
||||
<ToggleButton class="q-mr-sm" v-model="toggleWorker" />
|
||||
{{ toggleWorker ? $t('general.specify') : $t('general.noSpecify') }}
|
||||
</template>
|
||||
</div>
|
||||
<nav class="q-ml-auto">
|
||||
<AddButton
|
||||
v-if="!hideBtnAddWorker"
|
||||
icon-only
|
||||
@click.stop="$emit('addWorker')"
|
||||
/>
|
||||
</nav>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<div class="surface-1 q-pa-md full-width">
|
||||
<WorkerItem
|
||||
@update:employee-amount="(v) => $emit('update:employeeAmount', v)"
|
||||
:employee-amount
|
||||
:readonly="readonly"
|
||||
fallback-img="/images/employee-avatar.png"
|
||||
:rows="rowWorker"
|
||||
@delete="(i) => $emit('delete', i)"
|
||||
/>
|
||||
</div>
|
||||
</q-expansion-item>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue