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:
Methapon Metanipat 2025-01-14 09:08:31 +07:00 committed by GitHub
parent 0c694dee5d
commit 5e2100eb8d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 2897 additions and 77 deletions

View 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 },
};
});

View 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',
}

View file

@ -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,

View file

@ -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;

View file

@ -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,

View file

@ -52,6 +52,8 @@ export type RequestWork = {
productServiceId: string;
request: RequestData;
attributes?: Attributes;
creditNoteId?: string;
processByUserId?: string;
};
export type RowDocument = {

View file

@ -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(