feat: add view doc
This commit is contained in:
parent
1638ac35bb
commit
ab26a52c48
9 changed files with 1181 additions and 4 deletions
|
|
@ -31,6 +31,7 @@ import useOptionStore from 'src/stores/options';
|
||||||
import { dialogWarningClose } from 'src/stores/utils';
|
import { dialogWarningClose } from 'src/stores/utils';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { QForm } from 'quasar';
|
import { QForm } from 'quasar';
|
||||||
|
import { getName } from 'src/services/keycloak';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -89,13 +90,13 @@ const pageState = reactive({
|
||||||
const formData = ref<CreditNotePayload>({
|
const formData = ref<CreditNotePayload>({
|
||||||
quotationId: '',
|
quotationId: '',
|
||||||
requestWorkId: [],
|
requestWorkId: [],
|
||||||
remark: '',
|
|
||||||
reason: '',
|
reason: '',
|
||||||
detail: '',
|
detail: '',
|
||||||
paybackType: 'Cash',
|
paybackType: 'Cash',
|
||||||
paybackBank: '',
|
paybackBank: '',
|
||||||
paybackAccount: '',
|
paybackAccount: '',
|
||||||
paybackAccountName: '',
|
paybackAccountName: '',
|
||||||
|
remark: '#[quotation-labor]<br/><br/>#[quotation-payment]',
|
||||||
});
|
});
|
||||||
|
|
||||||
const formTaskList = ref<
|
const formTaskList = ref<
|
||||||
|
|
@ -494,6 +495,44 @@ function fileToUrl(file: File) {
|
||||||
return URL.createObjectURL(file);
|
return URL.createObjectURL(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function storeDataLocal() {
|
||||||
|
localStorage.setItem(
|
||||||
|
'credit-note-preview',
|
||||||
|
JSON.stringify({
|
||||||
|
data: {
|
||||||
|
...formData.value,
|
||||||
|
id:
|
||||||
|
route.name === 'CreditNoteNew' ? undefined : creditNoteData.value?.id,
|
||||||
|
customerBranchId: quotationData.value?.customerBranchId,
|
||||||
|
registeredBranchId: quotationData.value?.registeredBranchId,
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
source: {
|
||||||
|
code:
|
||||||
|
route.name === 'CreditNoteNew' ? '-' : creditNoteData.value?.code,
|
||||||
|
createAt:
|
||||||
|
route.name === 'CreditNoteNew'
|
||||||
|
? Date.now()
|
||||||
|
: creditNoteData.value?.createdAt,
|
||||||
|
createBy: creditNoteData.value?.createdBy,
|
||||||
|
contactName: quotationData.value?.contactName,
|
||||||
|
contactTel: quotationData.value?.contactTel,
|
||||||
|
workName: quotationData.value?.workName,
|
||||||
|
},
|
||||||
|
summaryPrice: summaryPrice.value,
|
||||||
|
taskListGroup: { ...taskListGroup.value },
|
||||||
|
agentPrice: quotationData.value?.agentPrice,
|
||||||
|
|
||||||
|
createdBy: getName(),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const url = new URL('/credit-note/document-view', window.location.origin);
|
||||||
|
|
||||||
|
window.open(url, '_blank');
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
initTheme();
|
initTheme();
|
||||||
initLang();
|
initLang();
|
||||||
|
|
@ -757,7 +796,7 @@ onMounted(async () => {
|
||||||
outlined
|
outlined
|
||||||
icon="mdi-play-box-outline"
|
icon="mdi-play-box-outline"
|
||||||
color="207 96% 32%"
|
color="207 96% 32%"
|
||||||
@click="console.log('view example')"
|
@click="storeDataLocal()"
|
||||||
>
|
>
|
||||||
{{ $t('general.view', { msg: $t('general.example') }) }}
|
{{ $t('general.view', { msg: $t('general.example') }) }}
|
||||||
</MainButton>
|
</MainButton>
|
||||||
|
|
|
||||||
113
src/pages/11_credit-note/document-view/BankComponents.vue
Normal file
113
src/pages/11_credit-note/document-view/BankComponents.vue
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
import { QSelect } from 'quasar';
|
||||||
|
|
||||||
|
// NOTE: Import stores
|
||||||
|
import { selectFilterOptionRefMod } from 'stores/utils';
|
||||||
|
import useOptionStore from 'stores/options';
|
||||||
|
|
||||||
|
// NOTE Import Types
|
||||||
|
import { BankBook } from 'stores/branch/types';
|
||||||
|
|
||||||
|
// NOTE: Import Components
|
||||||
|
|
||||||
|
const optionStore = useOptionStore();
|
||||||
|
|
||||||
|
const bankBookOptions = ref<Record<string, unknown>[]>([]);
|
||||||
|
let bankBookFilter: (
|
||||||
|
value: string,
|
||||||
|
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
bankBook: BankBook;
|
||||||
|
index: number;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => optionStore.globalOption,
|
||||||
|
() => {
|
||||||
|
bankBookFilter = selectFilterOptionRefMod(
|
||||||
|
ref(optionStore.globalOption.bankBook),
|
||||||
|
bankBookOptions,
|
||||||
|
'label',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<fieldset class="rounded" style="border: 1px solid var(--gray-4)">
|
||||||
|
<legend>ช่องทางที่ {{ index + 1 }}</legend>
|
||||||
|
|
||||||
|
<div class="border-5 full-width row" style="gap: var(--size-4)">
|
||||||
|
<div class="column q-pa-sm" style="width: fit-content">
|
||||||
|
<img
|
||||||
|
:src="bankBook.bankUrl"
|
||||||
|
class="rounded"
|
||||||
|
style="
|
||||||
|
border: 1px solid var(--gray-3);
|
||||||
|
object-fit: scale-down;
|
||||||
|
width: 1in;
|
||||||
|
height: 1in;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="column col" style="gap: var(--size-3)">
|
||||||
|
<div class="row" style="justify-content: space-between">
|
||||||
|
<div class="text-with-label">
|
||||||
|
<div>เลขบัญชีธนาคาร</div>
|
||||||
|
<div class="row items-start">
|
||||||
|
<img
|
||||||
|
width="25px"
|
||||||
|
height="25px"
|
||||||
|
class="q-mr-xs"
|
||||||
|
:src="`/img/bank/${bankBook.bankName}.png`"
|
||||||
|
/>
|
||||||
|
{{ bankBook.accountNumber }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-with-label">
|
||||||
|
<div>เลขบัญชีธนาคาร</div>
|
||||||
|
<div>{{ bankBook.accountNumber }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-with-label">
|
||||||
|
<div>ชื่อบัญชี</div>
|
||||||
|
<div>{{ bankBook.accountName }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="text-with-label">
|
||||||
|
<div>สาขา</div>
|
||||||
|
<div>{{ bankBook.bankBranch }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
legend {
|
||||||
|
background-color: white;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bordered-2 {
|
||||||
|
border: 2px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-5 {
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-with-label {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
& > :first-child {
|
||||||
|
color: var(--gray-6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
638
src/pages/11_credit-note/document-view/MainPage.vue
Normal file
638
src/pages/11_credit-note/document-view/MainPage.vue
Normal file
|
|
@ -0,0 +1,638 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { onMounted, nextTick, ref, watch } from 'vue';
|
||||||
|
import { precisionRound } from 'src/utils/arithmetic';
|
||||||
|
import ThaiBahtText from 'thai-baht-text';
|
||||||
|
|
||||||
|
// NOTE: Import stores
|
||||||
|
import { formatNumberDecimal } from 'stores/utils';
|
||||||
|
|
||||||
|
import { useConfigStore } from 'stores/config';
|
||||||
|
import useBranchStore from 'stores/branch';
|
||||||
|
import { baseUrl } from 'stores/utils';
|
||||||
|
import useCustomerStore from 'stores/customer';
|
||||||
|
|
||||||
|
import { CreditNotePayload, useCreditNote } from 'src/stores/credit-note';
|
||||||
|
|
||||||
|
// NOTE Import Types
|
||||||
|
import { CustomerBranch } from 'stores/customer/types';
|
||||||
|
import { BankBook, Branch } from 'stores/branch/types';
|
||||||
|
import { CustomerBranchRelation, Details } from 'src/stores/quotations/types';
|
||||||
|
|
||||||
|
// NOTE: Import Components
|
||||||
|
import ViewPDF from './ViewPdf.vue';
|
||||||
|
import ViewHeader from './ViewHeader.vue';
|
||||||
|
import ViewFooter from './ViewFooter.vue';
|
||||||
|
import BankComponents from './BankComponents.vue';
|
||||||
|
import PrintButton from 'src/components/button/PrintButton.vue';
|
||||||
|
import { convertTemplate } from 'src/utils/string-template';
|
||||||
|
import { RequestWork } from 'src/stores/request-list';
|
||||||
|
import { Employee } from 'src/stores/employee/types';
|
||||||
|
|
||||||
|
const configStore = useConfigStore();
|
||||||
|
const branchStore = useBranchStore();
|
||||||
|
const customerStore = useCustomerStore();
|
||||||
|
const creditNoteStore = useCreditNote();
|
||||||
|
const { data: config } = storeToRefs(configStore);
|
||||||
|
|
||||||
|
const agentPrice = ref<boolean>(false);
|
||||||
|
|
||||||
|
type SummaryPrice = {
|
||||||
|
totalPrice: number;
|
||||||
|
totalDiscount: number;
|
||||||
|
vat: number;
|
||||||
|
vatExcluded: number;
|
||||||
|
finalPrice: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const customer = ref<CustomerBranch>();
|
||||||
|
const branch = ref<Branch>();
|
||||||
|
const bankList = ref<BankBook[]>([]);
|
||||||
|
const worker = ref<Employee[]>([]);
|
||||||
|
const taskListGroup = ref<
|
||||||
|
{
|
||||||
|
product: RequestWork['productService']['product'];
|
||||||
|
list: RequestWork[];
|
||||||
|
}[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const elements = ref<HTMLElement[]>([]);
|
||||||
|
const chunks = ref<
|
||||||
|
{
|
||||||
|
product: RequestWork['productService']['product'];
|
||||||
|
list: RequestWork[];
|
||||||
|
}[][]
|
||||||
|
>([[]]);
|
||||||
|
const attachmentList = ref<
|
||||||
|
{
|
||||||
|
url: string;
|
||||||
|
isImage?: boolean;
|
||||||
|
isPDF?: boolean;
|
||||||
|
}[]
|
||||||
|
>([]);
|
||||||
|
const data = ref<
|
||||||
|
CreditNotePayload & {
|
||||||
|
id?: string;
|
||||||
|
customerBranch: CustomerBranchRelation;
|
||||||
|
customerBranchId: string;
|
||||||
|
registeredBranchId: string;
|
||||||
|
}
|
||||||
|
>();
|
||||||
|
|
||||||
|
const summaryPrice = ref<SummaryPrice>({
|
||||||
|
totalPrice: 0,
|
||||||
|
totalDiscount: 0,
|
||||||
|
vat: 0,
|
||||||
|
vatExcluded: 0,
|
||||||
|
finalPrice: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function getAttachment(quotationId: string) {
|
||||||
|
const attachment = await creditNoteStore.listAttachment({
|
||||||
|
parentId: quotationId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (attachment) {
|
||||||
|
attachmentList.value = await Promise.all(
|
||||||
|
attachment.map(async (v) => {
|
||||||
|
const url = await creditNoteStore.getAttachment({
|
||||||
|
parentId: quotationId,
|
||||||
|
name: v,
|
||||||
|
});
|
||||||
|
const ft = v.substring(v.lastIndexOf('.') + 1);
|
||||||
|
|
||||||
|
return {
|
||||||
|
url,
|
||||||
|
isImage: ['png', 'jpg', 'jpeg'].includes(ft),
|
||||||
|
isPDF: ft === 'pdf',
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function assignData() {
|
||||||
|
for (let i = 0; i < taskListGroup.value.length; i++) {
|
||||||
|
let el = elements.value.at(-1);
|
||||||
|
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
if (getHeight(el) < 500) {
|
||||||
|
chunks.value.at(-1)?.push(taskListGroup.value[i]);
|
||||||
|
} else {
|
||||||
|
chunks.value.push([]);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHeight(el: HTMLElement) {
|
||||||
|
const shadow = document.createElement('div');
|
||||||
|
|
||||||
|
shadow.style.opacity = '0';
|
||||||
|
shadow.style.position = 'absolute';
|
||||||
|
shadow.style.top = '-999999px';
|
||||||
|
shadow.style.left = '-999999px';
|
||||||
|
shadow.style.pointerEvents = 'none';
|
||||||
|
|
||||||
|
document.body.appendChild(shadow);
|
||||||
|
|
||||||
|
shadow.appendChild(el.cloneNode(true));
|
||||||
|
|
||||||
|
const height = shadow.offsetHeight;
|
||||||
|
|
||||||
|
document.body.removeChild(shadow);
|
||||||
|
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
const details = ref<Details>();
|
||||||
|
enum View {
|
||||||
|
CreditNote,
|
||||||
|
Invoice,
|
||||||
|
Payment,
|
||||||
|
Receipt,
|
||||||
|
}
|
||||||
|
const view = ref<View>(View.CreditNote);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
let str =
|
||||||
|
localStorage.getItem('credit-note-preview') ||
|
||||||
|
sessionStorage.getItem('credit-note-preview');
|
||||||
|
|
||||||
|
if (!str) return;
|
||||||
|
|
||||||
|
const obj: CreditNotePayload = JSON.parse(str);
|
||||||
|
|
||||||
|
if (obj) sessionStorage.setItem('credit-note-preview', JSON.stringify(obj));
|
||||||
|
|
||||||
|
delete localStorage['credit-note-preview'];
|
||||||
|
|
||||||
|
const storedData = sessionStorage.getItem('credit-note-preview');
|
||||||
|
|
||||||
|
const parsed = storedData ? JSON.parse(storedData) : {};
|
||||||
|
|
||||||
|
data.value = 'data' in parsed ? parsed.data : undefined;
|
||||||
|
|
||||||
|
if (data.value) {
|
||||||
|
if (data.value.id) {
|
||||||
|
await getAttachment(data.value.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const resCustomerBranch = await customerStore.getBranchById(
|
||||||
|
data.value.customerBranchId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (resCustomerBranch) {
|
||||||
|
customer.value = resCustomerBranch;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.meta.taskListGroup !== undefined) {
|
||||||
|
taskListGroup.value = Object.values(parsed.meta.taskListGroup) || [];
|
||||||
|
|
||||||
|
worker.value = [];
|
||||||
|
taskListGroup.value.forEach((v) => {
|
||||||
|
worker.value = worker.value.concat(
|
||||||
|
v.list.map((x) => x.request.employee),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
details.value = {
|
||||||
|
code: parsed.meta.source.code,
|
||||||
|
createdAt: parsed.meta.source.createdAt,
|
||||||
|
createdBy: `${parsed.meta.createdBy} ${!parsed.meta.source.createdBy ? '' : parsed.meta.source.createdBy.telephoneNo}`,
|
||||||
|
payCondition: parsed.meta.source.payCondition,
|
||||||
|
contactName: parsed.meta.source.contactName,
|
||||||
|
contactTel: parsed.meta.source.contactTel,
|
||||||
|
workName: parsed.meta.source.workName,
|
||||||
|
dueDate: parsed.meta.source.dueDate,
|
||||||
|
worker: worker.value,
|
||||||
|
};
|
||||||
|
|
||||||
|
agentPrice.value = parsed.meta.agentPrice;
|
||||||
|
|
||||||
|
const resBranch = await branchStore.fetchById(
|
||||||
|
data.value?.registeredBranchId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (resBranch) {
|
||||||
|
branch.value = resBranch;
|
||||||
|
|
||||||
|
bankList.value = resBranch.bank.map((v) => ({
|
||||||
|
...v,
|
||||||
|
bankUrl: `${baseUrl}/branch/${resBranch.id}/bank-qr/${v.id}?ts=${Date.now()}`,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
summaryPrice.value = parsed.meta.summaryPrice;
|
||||||
|
|
||||||
|
assignData();
|
||||||
|
});
|
||||||
|
|
||||||
|
function calcPricePerUnit(product: RequestWork['productService']['product']) {
|
||||||
|
return product.vatIncluded
|
||||||
|
? (agentPrice.value ? product.agentPrice : product.price) /
|
||||||
|
(1 + (config.value?.vat || 0.07))
|
||||||
|
: agentPrice.value
|
||||||
|
? product.agentPrice
|
||||||
|
: product.price;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcPrice(
|
||||||
|
product: RequestWork['productService']['product'],
|
||||||
|
amount: number,
|
||||||
|
) {
|
||||||
|
const pricePerUnit = agentPrice.value ? product.agentPrice : product.price;
|
||||||
|
|
||||||
|
const priceNoVat = product.vatIncluded
|
||||||
|
? pricePerUnit / (1 + (config.value?.vat || 0.07))
|
||||||
|
: pricePerUnit;
|
||||||
|
const priceDiscountNoVat = priceNoVat * amount - 0;
|
||||||
|
|
||||||
|
const rawVatTotal = priceDiscountNoVat * (config.value?.vat || 0.07);
|
||||||
|
|
||||||
|
return precisionRound(priceNoVat * amount + rawVatTotal);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(elements, () => {});
|
||||||
|
|
||||||
|
function print() {
|
||||||
|
window.print();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="toolbar">
|
||||||
|
<PrintButton solid @click="print" />
|
||||||
|
</div>
|
||||||
|
<div class="row justify-between container color-debit-note">
|
||||||
|
<section class="content" v-for="chunk in chunks">
|
||||||
|
<ViewHeader
|
||||||
|
v-if="!!branch && !!customer && !!details"
|
||||||
|
:branch="branch"
|
||||||
|
:customer="customer"
|
||||||
|
:details="details"
|
||||||
|
:view="view"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span
|
||||||
|
class="q-mb-sm q-mt-md"
|
||||||
|
style="
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--main);
|
||||||
|
display: block;
|
||||||
|
border-bottom: 2px solid var(--main);
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ $t('preview.productList') }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<table ref="elements" class="q-mb-sm" cellpadding="0" style="width: 100%">
|
||||||
|
<tbody class="color-tr">
|
||||||
|
<tr>
|
||||||
|
<th>{{ $t('preview.rank') }}</th>
|
||||||
|
<th>{{ $t('preview.productCode') }}</th>
|
||||||
|
<th>{{ $t('general.detail') }}</th>
|
||||||
|
<th>{{ $t('preview.pricePerUnit') }}</th>
|
||||||
|
<th>{{ $t('preview.value') }}</th>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="(v, i) in chunk">
|
||||||
|
<td class="text-center">{{ i + 1 }}</td>
|
||||||
|
<td>{{ v.product.code }}</td>
|
||||||
|
<td>{{ v.product.name }}</td>
|
||||||
|
<td style="text-align: center">
|
||||||
|
{{
|
||||||
|
formatNumberDecimal(
|
||||||
|
calcPricePerUnit(v.product) +
|
||||||
|
(v.product.calcVat
|
||||||
|
? calcPricePerUnit(v.product) * (config?.vat || 0.07)
|
||||||
|
: 0),
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</td>
|
||||||
|
<td style="text-align: center">
|
||||||
|
{{ formatNumberDecimal(calcPrice(v.product, v.list.length), 2) }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table
|
||||||
|
style="width: 40%; margin-left: auto"
|
||||||
|
class="q-mb-md"
|
||||||
|
cellpadding="0"
|
||||||
|
>
|
||||||
|
<tbody class="color-tr">
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('general.total') }}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{{ formatNumberDecimal(summaryPrice.totalPrice, 2) }}
|
||||||
|
฿
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('general.discount') }}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{{ formatNumberDecimal(summaryPrice.totalDiscount, 2) || 0 }} ฿
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('general.totalAfterDiscount') }}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{{
|
||||||
|
formatNumberDecimal(
|
||||||
|
summaryPrice.totalPrice - summaryPrice.totalDiscount,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
฿
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('general.totalVatExcluded') }}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{{ formatNumberDecimal(summaryPrice.vatExcluded, 2) || 0 }}
|
||||||
|
฿
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('general.vat', { msg: '7%' }) }}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{{ formatNumberDecimal(summaryPrice.vat, 2) }} ฿
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('general.totalVatIncluded') }}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{{
|
||||||
|
formatNumberDecimal(
|
||||||
|
summaryPrice.totalPrice -
|
||||||
|
summaryPrice.totalDiscount +
|
||||||
|
summaryPrice.vat,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
฿
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('general.discountAfterVat') }}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{{ formatNumberDecimal(data?.discount || 0, 2) }}
|
||||||
|
฿
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="row justify-between q-mb-md" style="width: 100%">
|
||||||
|
<div
|
||||||
|
class="column set-width bg-color full-height"
|
||||||
|
style="padding: 12px"
|
||||||
|
>
|
||||||
|
({{ ThaiBahtText(summaryPrice.finalPrice) }})
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="row text-right border-5 items-center"
|
||||||
|
style="width: 40%; background: var(--main); padding: 8px"
|
||||||
|
>
|
||||||
|
<span style="color: white; font-weight: 600">ยอดรวมสุทธิ</span>
|
||||||
|
<span
|
||||||
|
class="border-5"
|
||||||
|
style="
|
||||||
|
width: 70%;
|
||||||
|
margin-left: auto;
|
||||||
|
background: white;
|
||||||
|
padding: 4px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
formatNumberDecimal(Math.max(summaryPrice.finalPrice, 0), 2) || 0
|
||||||
|
}}
|
||||||
|
฿
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="content">
|
||||||
|
<ViewHeader
|
||||||
|
v-if="!!branch && !!customer && !!details"
|
||||||
|
:branch="branch"
|
||||||
|
:customer="customer"
|
||||||
|
:details="details"
|
||||||
|
:view="view"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span
|
||||||
|
class="q-mb-sm q-mt-md"
|
||||||
|
style="
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--main);
|
||||||
|
display: block;
|
||||||
|
border-bottom: 2px solid var(--main);
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ $t('general.remark') }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="border-5 surface-0 detail-note q-mb-md"
|
||||||
|
style="width: 100%; padding: 8px 16px; white-space: pre-wrap"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-html="
|
||||||
|
convertTemplate(data?.remark || '', {
|
||||||
|
'quotation-payment': {
|
||||||
|
paymentType: 'Full',
|
||||||
|
amount: summaryPrice.finalPrice,
|
||||||
|
},
|
||||||
|
'quotation-labor': {
|
||||||
|
name:
|
||||||
|
details?.worker.map(
|
||||||
|
(v, i) =>
|
||||||
|
`${i + 1}. ` +
|
||||||
|
`${v.namePrefix}. ${v.firstNameEN} ${v.lastNameEN}`.toUpperCase(),
|
||||||
|
) || [],
|
||||||
|
},
|
||||||
|
}) || '-'
|
||||||
|
"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="content">
|
||||||
|
<ViewHeader
|
||||||
|
v-if="!!branch && !!customer && !!details"
|
||||||
|
:branch="branch"
|
||||||
|
:customer="customer"
|
||||||
|
:details="details"
|
||||||
|
:view="view"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span
|
||||||
|
class="q-mb-sm"
|
||||||
|
style="
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--main);
|
||||||
|
display: block;
|
||||||
|
border-bottom: 2px solid var(--main);
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ $t('preview.paymentMethods') }}
|
||||||
|
</span>
|
||||||
|
<article style="height: 5.8in">
|
||||||
|
<BankComponents
|
||||||
|
v-for="(bank, index) in bankList"
|
||||||
|
:index="index"
|
||||||
|
:bank-book="bank"
|
||||||
|
:key="bank.id"
|
||||||
|
/>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<ViewFooter
|
||||||
|
:data="{
|
||||||
|
name: '',
|
||||||
|
company: branch?.name || '',
|
||||||
|
buyer: '',
|
||||||
|
buyDate: '',
|
||||||
|
approveDate: '',
|
||||||
|
approver: '',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section
|
||||||
|
v-for="item in attachmentList.filter((v) => v.isImage)"
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
<q-img :src="item.url" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<ViewPDF
|
||||||
|
v-for="item in attachmentList.filter((v) => v.isPDF)"
|
||||||
|
:url="item.url"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.color-debit-note {
|
||||||
|
--main: var(--indigo-10);
|
||||||
|
--main-hsl: var(--indigo-10-hsl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
width: 100%;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
background: white;
|
||||||
|
border-bottom: 1px solid var(--gray-3);
|
||||||
|
z-index: 99999;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: var(--main);
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-5 {
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.set-width {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-color {
|
||||||
|
background-color: hsla(var(--main-hsl) / 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-tr > tr:nth-child(odd) {
|
||||||
|
background-color: hsla(var(--main-hsl) / 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 1rem;
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-inline: auto;
|
||||||
|
background: var(--gray-3);
|
||||||
|
width: calc(8.3in + 1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container :deep(*) {
|
||||||
|
font-size: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.5in;
|
||||||
|
align-items: center;
|
||||||
|
background: white;
|
||||||
|
page-break-after: always;
|
||||||
|
break-after: page;
|
||||||
|
height: 11.7in;
|
||||||
|
max-height: 11.7in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-bottom {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-note {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
& > * {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border-style: solid;
|
||||||
|
border-color: var(--main);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
.toolbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 0;
|
||||||
|
gap: 0;
|
||||||
|
width: 100%;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: 0;
|
||||||
|
height: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
98
src/pages/11_credit-note/document-view/ViewFooter.vue
Normal file
98
src/pages/11_credit-note/document-view/ViewFooter.vue
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
data?: {
|
||||||
|
name: string;
|
||||||
|
buyer: string;
|
||||||
|
buyDate: string;
|
||||||
|
|
||||||
|
company: string;
|
||||||
|
approver: string;
|
||||||
|
approveDate: string;
|
||||||
|
};
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="footer-container">
|
||||||
|
<div class="footer-top">
|
||||||
|
<div>ในนาม {{ data?.name || '-' }}</div>
|
||||||
|
<div>ในนาม {{ data?.company || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<img src="/images/jws-stamp.png" alt="${0}" />
|
||||||
|
|
||||||
|
<div class="footer-bottom">
|
||||||
|
<section>
|
||||||
|
<div>
|
||||||
|
<span class="data-placeholder"></span>
|
||||||
|
<span>ผู้สั่งซื้อสินค้า</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="data-placeholder"></span>
|
||||||
|
<span>วันที่</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<div>
|
||||||
|
<span class="data-placeholder"></span>
|
||||||
|
<span>ผู้อนุมัติ</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="data-placeholder"></span>
|
||||||
|
<span>วันที่</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.footer-container {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
justify-content: center;
|
||||||
|
height: 1.5in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-top {
|
||||||
|
position: absolute;
|
||||||
|
width: 100;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 1rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
width: 38%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.footer-bottom {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
display: flex;
|
||||||
|
width: 38%;
|
||||||
|
justify-content: space-around;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-placeholder {
|
||||||
|
display: block;
|
||||||
|
min-width: 1.2in;
|
||||||
|
border-bottom: 1px dotted black;
|
||||||
|
margin-bottom: 2mm;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
192
src/pages/11_credit-note/document-view/ViewHeader.vue
Normal file
192
src/pages/11_credit-note/document-view/ViewHeader.vue
Normal file
|
|
@ -0,0 +1,192 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { dateFormat } from 'src/utils/datetime';
|
||||||
|
|
||||||
|
// NOTE: Import stores
|
||||||
|
import { formatAddress } from 'src/utils/address';
|
||||||
|
|
||||||
|
// NOTE Import Types
|
||||||
|
import { Branch } from 'src/stores/branch/types';
|
||||||
|
import { CustomerBranchRelation, Details } from 'src/stores/quotations/types';
|
||||||
|
// NOTE: Import Components
|
||||||
|
|
||||||
|
enum View {
|
||||||
|
CreditNote,
|
||||||
|
Invoice,
|
||||||
|
Payment,
|
||||||
|
Receipt,
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
branch: Branch;
|
||||||
|
customer: CustomerBranchRelation;
|
||||||
|
details: Details;
|
||||||
|
view: View;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
function titleMode(mode: View): string {
|
||||||
|
if (mode === View.CreditNote) {
|
||||||
|
return 'preview.title.creditNote';
|
||||||
|
}
|
||||||
|
if (mode === View.Invoice) {
|
||||||
|
return 'preview.title.invoice';
|
||||||
|
}
|
||||||
|
if (mode === View.Payment) {
|
||||||
|
return 'preview.title.payment';
|
||||||
|
}
|
||||||
|
if (mode === View.Receipt) {
|
||||||
|
return 'preview.title.receipt';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="row items-center q-mb-lg">
|
||||||
|
<div class="column" style="width: 50%">
|
||||||
|
<img src="/logo.png" width="192px" style="object-fit: scale-down" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="column"
|
||||||
|
style="text-align: center; width: 50%; font-weight: 800; font-size: 24px"
|
||||||
|
>
|
||||||
|
{{ $t(titleMode(view)) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<article class="detail-card">
|
||||||
|
<section class="detail-customer-info">
|
||||||
|
<article>
|
||||||
|
<b>
|
||||||
|
{{ !!branch.virtual ? '' : $t('general.company') }} {{ branch.name }}
|
||||||
|
</b>
|
||||||
|
|
||||||
|
<span v-if="branch.province && branch.district && branch.subDistrict">
|
||||||
|
{{
|
||||||
|
formatAddress({
|
||||||
|
address: branch.address,
|
||||||
|
addressEN: branch.addressEN,
|
||||||
|
moo: branch.moo,
|
||||||
|
mooEN: branch.mooEN,
|
||||||
|
soi: branch.soi,
|
||||||
|
soiEN: branch.soiEN,
|
||||||
|
street: branch.street,
|
||||||
|
streetEN: branch.streetEN,
|
||||||
|
province: branch.province,
|
||||||
|
district: branch.district,
|
||||||
|
subDistrict: branch.subDistrict,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<span>เลขประจำตัวผู้เสียภาษี {{ branch.taxNo }}</span>
|
||||||
|
<span>เบอร์โทร {{ branch.telephoneNo }}</span>
|
||||||
|
<span>{{ branch.webUrl }}</span>
|
||||||
|
</article>
|
||||||
|
<article>
|
||||||
|
<b>ลูกค้า</b>
|
||||||
|
<span>
|
||||||
|
{{
|
||||||
|
formatAddress({
|
||||||
|
address: customer.address,
|
||||||
|
addressEN: customer.addressEN,
|
||||||
|
moo: customer.moo,
|
||||||
|
mooEN: customer.mooEN,
|
||||||
|
soi: customer.soi,
|
||||||
|
soiEN: customer.soiEN,
|
||||||
|
street: customer.street,
|
||||||
|
streetEN: customer.streetEN,
|
||||||
|
province: customer.province,
|
||||||
|
district: customer.district,
|
||||||
|
subDistrict: customer.subDistrict,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<span>เลขประจำตัวผู้เสียภาษี {{ customer.citizenId }}</span>
|
||||||
|
<span>เบอร์โทร {{ customer.telephoneNo }}</span>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
<section class="detail-quotation-info">
|
||||||
|
<div>
|
||||||
|
<div>{{ $t('general.itemNo', { msg: `${$t(titleMode(view))}` }) }}</div>
|
||||||
|
<div>{{ details.code }}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>{{ $t('preview.dateAt', { msg: `${$t(titleMode(view))}` }) }}</div>
|
||||||
|
<div>{{ dateFormat(details.createdAt, true, false, true) }}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>{{ $t('preview.seller') }}</div>
|
||||||
|
<div>{{ details.createdBy }}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>{{ $t('quotation.paymentCondition') }}</div>
|
||||||
|
<div>
|
||||||
|
{{
|
||||||
|
{
|
||||||
|
Full: $t('quotation.type.fullAmountCash'),
|
||||||
|
Split: $t('quotation.type.installmentsCash'),
|
||||||
|
BillFull: $t('quotation.type.fullAmountBill'),
|
||||||
|
BillSplit: $t('quotation.type.installmentsBill'),
|
||||||
|
}[details.payCondition]
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>{{ $t('quotation.workName') }}</div>
|
||||||
|
<div>{{ details.workName ? details.workName : `-` }}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>{{ $t('quotation.contactName') }}</div>
|
||||||
|
<div>{{ details.contactName ? details.contactName : `-` }}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>{{ $t('preview.dueDate') }}</div>
|
||||||
|
<div>{{ dateFormat(details.dueDate, true, false, true) }}</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.detail-card {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > :first-child {
|
||||||
|
max-width: 57.5%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-customer-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
& > :first-child {
|
||||||
|
color: var(--main);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-quotation-info {
|
||||||
|
& > * {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
& > :first-child {
|
||||||
|
color: var(--main);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
27
src/pages/11_credit-note/document-view/ViewPdf.vue
Normal file
27
src/pages/11_credit-note/document-view/ViewPdf.vue
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { VuePDF, usePDF } from '@tato30/vue-pdf';
|
||||||
|
const props = defineProps<{
|
||||||
|
url: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { pdf, pages } = usePDF(props.url);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<section v-for="page in pages" class="content">
|
||||||
|
<VuePDF style="width: 100%" :pdf="pdf" :page="page" :scale="1.5" />
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.content :deep(canvas) {
|
||||||
|
width: 100% !important;
|
||||||
|
height: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
.content :deep(canvas) {
|
||||||
|
scale: 1.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
67
src/pages/11_credit-note/expansion/RemarkExpansion.vue
Normal file
67
src/pages/11_credit-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>
|
||||||
|
|
@ -173,6 +173,11 @@ const routes: RouteRecordRaw[] = [
|
||||||
name: 'CreditNoteView',
|
name: 'CreditNoteView',
|
||||||
component: () => import('pages/11_credit-note/FormPage.vue'),
|
component: () => import('pages/11_credit-note/FormPage.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/credit-note/document-view',
|
||||||
|
name: 'CreditNoteDocumentView',
|
||||||
|
component: () => import('pages/11_credit-note/document-view/MainPage.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/receipt/:id',
|
path: '/receipt/:id',
|
||||||
name: 'receiptform',
|
name: 'receiptform',
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import { CreatedBy } from '../types';
|
||||||
export type CreditNotePayload = {
|
export type CreditNotePayload = {
|
||||||
quotationId: string;
|
quotationId: string;
|
||||||
requestWorkId: string[];
|
requestWorkId: string[];
|
||||||
remark?: string;
|
|
||||||
reason: string;
|
reason: string;
|
||||||
detail: string;
|
detail: string;
|
||||||
paybackType: 'BankTransfer' | 'Cash';
|
paybackType: 'BankTransfer' | 'Cash';
|
||||||
|
|
@ -23,7 +22,6 @@ export type CreditNote = {
|
||||||
quotationId: string;
|
quotationId: string;
|
||||||
quotation: QuotationFull;
|
quotation: QuotationFull;
|
||||||
requestWork: RequestWork[];
|
requestWork: RequestWork[];
|
||||||
remark?: string;
|
|
||||||
reason: string;
|
reason: string;
|
||||||
detail: string;
|
detail: string;
|
||||||
paybackType: 'BankTransfer' | 'Cash';
|
paybackType: 'BankTransfer' | 'Cash';
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue