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
103
src/stores/credit-note/index.ts
Normal file
103
src/stores/credit-note/index.ts
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import {
|
||||
CreditNote as Data,
|
||||
CreditNoteStatus as Status,
|
||||
CreditNotePayload as Payload,
|
||||
CreditNotePaybackStatus,
|
||||
} from './types.ts';
|
||||
import { ref } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import { api } from 'src/boot/axios.ts';
|
||||
import { PaginationResult } from 'src/types.ts';
|
||||
import { manageAttachment, manageFile } from '../utils/index.ts';
|
||||
|
||||
const ENDPOINT = 'credit-note';
|
||||
|
||||
export * from './types.ts';
|
||||
|
||||
export async function getCreditNoteStats() {
|
||||
const res = await api.get<Record<Status, number>>(`/${ENDPOINT}/stats`);
|
||||
if (res.status < 400) {
|
||||
return res.data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function getCreditNoteList(params?: {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
query?: string;
|
||||
creditNoteStatus?: Status;
|
||||
}) {
|
||||
const res = await api.get<PaginationResult<Data>>(`/${ENDPOINT}`, {
|
||||
params,
|
||||
});
|
||||
if (res.status < 400) return res.data;
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function getCreditNote(id: string) {
|
||||
const res = await api.get<Data>(`/${ENDPOINT}/${id}`);
|
||||
if (res.status < 400) return res.data;
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function createCreditNote(body: Payload) {
|
||||
const res = await api.post<Data>(`/${ENDPOINT}`, body);
|
||||
if (res.status < 400) return res.data;
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function updateCreditNote(id: string, body: Payload) {
|
||||
const res = await api.put<Data>(`/${ENDPOINT}/${id}`, body);
|
||||
if (res.status < 400) return res.data;
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function deleteCreditNote(id: string) {
|
||||
const res = await api.delete<Data>(`/${ENDPOINT}/${id}`);
|
||||
if (res.status < 400) return res.data;
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function updatePaybackStatus(
|
||||
creditNoteId: string,
|
||||
status: CreditNotePaybackStatus,
|
||||
) {
|
||||
const res = await api.post(`/${ENDPOINT}/${creditNoteId}/payback-status`, {
|
||||
paybackStatus: status,
|
||||
});
|
||||
if (res.status < 400) return true;
|
||||
return null;
|
||||
}
|
||||
|
||||
export const useCreditNote = defineStore('credit-note-store', () => {
|
||||
const data = ref<Data[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const pageMax = ref<number>(1);
|
||||
const pageSize = ref<number>(30);
|
||||
const stats = ref<Record<Status, number>>({
|
||||
[Status.Pending]: 0,
|
||||
[Status.Success]: 0,
|
||||
});
|
||||
|
||||
return {
|
||||
data,
|
||||
page,
|
||||
pageMax,
|
||||
pageSize,
|
||||
stats,
|
||||
|
||||
getCreditNoteStats,
|
||||
getCreditNote,
|
||||
getCreditNoteList,
|
||||
createCreditNote,
|
||||
updateCreditNote,
|
||||
deleteCreditNote,
|
||||
|
||||
...manageAttachment(api, ENDPOINT),
|
||||
...manageFile<'slip'>(api, ENDPOINT),
|
||||
|
||||
action: { updatePaybackStatus },
|
||||
};
|
||||
});
|
||||
51
src/stores/credit-note/types.ts
Normal file
51
src/stores/credit-note/types.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import { QuotationFull } from '../quotations';
|
||||
import { RequestWork } from '../request-list';
|
||||
import { CreatedBy } from '../types';
|
||||
|
||||
export type CreditNotePayload = {
|
||||
quotationId: string;
|
||||
requestWorkId: string[];
|
||||
reason: string;
|
||||
detail: string;
|
||||
paybackType: 'BankTransfer' | 'Cash';
|
||||
paybackBank: string;
|
||||
paybackAccount: string;
|
||||
paybackAccountName: string;
|
||||
};
|
||||
|
||||
export type CreditNote = {
|
||||
id: string;
|
||||
|
||||
code: string;
|
||||
|
||||
quotationId: string;
|
||||
quotation: QuotationFull;
|
||||
requestWork: RequestWork[];
|
||||
reason: string;
|
||||
detail: string;
|
||||
paybackType: 'BankTransfer' | 'Cash';
|
||||
paybackBank: string;
|
||||
paybackAccount: string;
|
||||
paybackAccountName: string;
|
||||
paybackStatus: CreditNotePaybackStatus;
|
||||
paybackDate?: string | null;
|
||||
|
||||
value: number;
|
||||
|
||||
createdAt: string;
|
||||
createdBy?: CreatedBy;
|
||||
createdByUserId?: string;
|
||||
|
||||
creditNoteStatus: CreditNoteStatus;
|
||||
};
|
||||
|
||||
export enum CreditNoteStatus {
|
||||
Pending = 'Pending',
|
||||
Success = 'Success',
|
||||
}
|
||||
|
||||
export enum CreditNotePaybackStatus {
|
||||
Pending = 'Pending',
|
||||
Verify = 'Verify',
|
||||
Done = 'Done',
|
||||
}
|
||||
|
|
@ -69,6 +69,8 @@ export const useQuotationStore = defineStore('quotation-store', () => {
|
|||
| 'Canceled';
|
||||
urgentFirst?: boolean;
|
||||
query?: string;
|
||||
hasCancel?: boolean;
|
||||
includeRegisteredBranch?: boolean;
|
||||
}) {
|
||||
const res = await api.get<PaginationResult<Quotation>>('/quotation', {
|
||||
params,
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ export type QuotationStats = {
|
|||
};
|
||||
|
||||
export type Quotation = {
|
||||
_count: { worker: number };
|
||||
_count: { worker: number; canceledWork: number };
|
||||
id: string;
|
||||
finalPrice: number;
|
||||
vat: number;
|
||||
|
|
@ -253,6 +253,7 @@ export type Quotation = {
|
|||
| 'canceled';
|
||||
|
||||
registeredBranchId: string;
|
||||
registeredBranch?: { id: string; name: string; nameEN: string; code: string };
|
||||
|
||||
customerBranchId: string;
|
||||
customerBranch: CustomerBranchRelation;
|
||||
|
|
@ -328,7 +329,7 @@ export type QuotationFull = {
|
|||
customerBranchId: string;
|
||||
customerBranch: CustomerBranchRelation;
|
||||
registeredBranchId: string;
|
||||
registeredBranch: { id: string; name: string };
|
||||
registeredBranch: { id: string; name: string; nameEN: string; code: string };
|
||||
|
||||
createdByUserId: string;
|
||||
createdAt: string | Date;
|
||||
|
|
|
|||
|
|
@ -223,6 +223,8 @@ export const useRequestList = defineStore('request-list', () => {
|
|||
pageSize?: number;
|
||||
workStatus?: RequestWorkStatus;
|
||||
readyToTask?: boolean;
|
||||
quotationId?: string;
|
||||
cancelOnly?: boolean;
|
||||
}) {
|
||||
const res = await api.get<PaginationResult<RequestWork>>('/request-work', {
|
||||
params,
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ export type RequestWork = {
|
|||
productServiceId: string;
|
||||
request: RequestData;
|
||||
attributes?: Attributes;
|
||||
creditNoteId?: string;
|
||||
processByUserId?: string;
|
||||
};
|
||||
|
||||
export type RowDocument = {
|
||||
|
|
|
|||
|
|
@ -355,13 +355,33 @@ export function manageFile<T extends string>(
|
|||
parentId: string;
|
||||
fileId: string;
|
||||
file: File;
|
||||
uploadUrl?: boolean;
|
||||
onUploadProgress?: (e: AxiosProgressEvent) => void;
|
||||
abortController?: AbortController;
|
||||
}) => {
|
||||
const res = await api.put(
|
||||
`/${base}/${opts.parentId}/file-${opts.group}/${opts.fileId}`,
|
||||
opts.file,
|
||||
{
|
||||
const res = opts.uploadUrl
|
||||
? await api.put(
|
||||
`/${base}/${opts.parentId}/file-${opts.group}/${opts.fileId}`,
|
||||
)
|
||||
: await api.put(
|
||||
`/${base}/${opts.parentId}/file-${opts.group}/${opts.fileId}`,
|
||||
opts.file,
|
||||
{
|
||||
headers: { 'Content-Type': opts.file.type },
|
||||
onUploadProgress: opts.onUploadProgress
|
||||
? opts.onUploadProgress
|
||||
: option?.onUploadProgress
|
||||
? option.onUploadProgress
|
||||
: (e) => console.log(e),
|
||||
signal: opts.abortController?.signal,
|
||||
},
|
||||
);
|
||||
|
||||
if (res.status >= 400) return false;
|
||||
|
||||
if (opts.uploadUrl && typeof res.data === 'string') {
|
||||
// NOTE: Must use axios instance or else CORS error.
|
||||
const uploadRes = await axios.put(res.data, opts.file, {
|
||||
headers: { 'Content-Type': opts.file.type },
|
||||
onUploadProgress: opts.onUploadProgress
|
||||
? opts.onUploadProgress
|
||||
|
|
@ -369,10 +389,12 @@ export function manageFile<T extends string>(
|
|||
? option.onUploadProgress
|
||||
: (e) => console.log(e),
|
||||
signal: opts.abortController?.signal,
|
||||
},
|
||||
);
|
||||
if (res.status < 400) return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
if (uploadRes.status >= 400) return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
delFile: async (opts: { group: T; parentId: string; fileId: string }) => {
|
||||
const res = await api.delete(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue