Skip to content

Commit

Permalink
merge feature/validation first before pushing successfully
Browse files Browse the repository at this point in the history
  • Loading branch information
jing12345678910 committed Sep 12, 2024
2 parents 1433124 + e759d0a commit 09adc7e
Show file tree
Hide file tree
Showing 21 changed files with 387 additions and 274 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ module.exports = {
},
],
'implicit-arrow-linebreak': 'off',
'react/no-is-mounted': 'off',
},
},
],
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "iSunFA",
"version": "0.8.1+10",
"version": "0.8.1+14",
"private": false,
"scripts": {
"dev": "next dev",
Expand Down
1 change: 0 additions & 1 deletion src/components/edit_bookmark_modal/edit_bookmark_modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ const EditBookmarkModal = ({ isModalVisible, modalVisibilityHandler }: IAddBookm
}, [isAddBookmarkModalVisible]);

const menuOptionClickHandler = (name: string) => {
// console.log('selectedBookmark', selectedBookmarkRef.current);
setSelectedBookmark((prevSelected) => {
if (prevSelected.includes(name)) {
return prevSelected.filter((item) => item !== name);
Expand Down
1 change: 1 addition & 0 deletions src/components/kyc/kyc_form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ const KYCForm = () => {
...intUploadDocuments,
};
const { isComplete, missingFields } = isKYCFormComplete(companyKYCForm);

if (isComplete) {
const { success, code } = await triggerUpload({
params: {
Expand Down
10 changes: 10 additions & 0 deletions src/constants/zod_schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import {
invoiceGetByIdValidator,
invoiceUpdateValidator,
} from '@/lib/utils/zod_schema/invoice';
import {
journalDeleteValidator,
journalGetByIdValidator,
journalListValidator,
} from '@/lib/utils/zod_schema/journal';
import { kycUploadValidator } from '@/lib/utils/zod_schema/kyc';
import {
ocrDeleteValidator,
ocrListValidator,
Expand Down Expand Up @@ -35,4 +41,8 @@ export const API_ZOD_SCHEMA = {
[APIName.INVOICE_GET_BY_ID]: invoiceGetByIdValidator,
[APIName.VOUCHER_CREATE]: voucherCreateValidator,
[APIName.VOUCHER_UPDATE]: voucherUpdateValidator,
[APIName.JOURNAL_LIST]: journalListValidator,
[APIName.JOURNAL_GET_BY_ID]: journalGetByIdValidator,
[APIName.JOURNAL_DELETE]: journalDeleteValidator,
[APIName.KYC_UPLOAD]: kycUploadValidator,
};
2 changes: 1 addition & 1 deletion src/interfaces/company_kyc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export interface ICompanyKYCForm {
[RegistrationInfoKeys.COUNTRY]: CountryOptions;
[RegistrationInfoKeys.LEGAL_STRUCTURE]: LegalStructureOptions;
[RegistrationInfoKeys.BUSINESS_REGISTRATION_NUMBER]: string;
[RegistrationInfoKeys.REGISTRATION_DATE]: string;
[RegistrationInfoKeys.REGISTRATION_DATE]: string; // Info: (20240912 - Murky) Maybe this suppose to be number?
[RegistrationInfoKeys.INDUSTRY]: IndustryOptions;
[ContactInfoKeys.KEY_CONTACT_PERSON]: string;
[ContactInfoKeys.CONTACT_PHONE]: string;
Expand Down
17 changes: 16 additions & 1 deletion src/lib/utils/repo/company_kyc.repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,22 @@ export async function createCompanyKYC(
const companyKYC: CompanyKYC = await prisma.companyKYC.create({
data: {
companyId,
...companyKYCForm,
// ...companyKYCForm,
legalName: companyKYCForm.legalName,
city: companyKYCForm.city,
zipCode: companyKYCForm.zipCode,
address: companyKYCForm.address,
representativeName: companyKYCForm.representativeName,
country: companyKYCForm.country,
structure: companyKYCForm.structure,
registrationNumber: companyKYCForm.registrationNumber,
registrationDate: companyKYCForm.registrationDate,
industry: companyKYCForm.industry,
contactPerson: companyKYCForm.contactPerson,
contactPhone: companyKYCForm.contactPhone,
contactEmail: companyKYCForm.contactEmail,
website: companyKYCForm.website,
representativeIdType: companyKYCForm.representativeIdType,
registrationCertificateFileId: companyKYCForm.registrationCertificateFileId,
taxCertificateFileId: companyKYCForm.taxCertificateFileId,
representativeIdCardFileId: companyKYCForm.representativeIdCardFileId,
Expand Down
15 changes: 8 additions & 7 deletions src/lib/utils/report/report_401_generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import {
} from '@/interfaces/report';
import { getCompanyKYCByCompanyId } from '@/lib/utils/repo/company_kyc.repo';
import { convertTimestampToROCDate } from '@/lib/utils/common';
import { STATUS_MESSAGE } from '@/constants/status_code';
import { listJournalFor401 } from '@/lib/utils/repo/journal.repo';
import { SPECIAL_ACCOUNTS } from '@/constants/account';
import { IJournalIncludeVoucherLineItemsInvoicePayment } from '@/interfaces/journal';
import { importsCategories, purchasesCategories, salesCategories } from '@/constants/invoice';
import { CompanyKYC } from '@prisma/client';

export default class Report401Generator extends ReportGenerator {
constructor(companyId: number, startDateInSecond: number, endDateInSecond: number) {
Expand Down Expand Up @@ -198,20 +198,21 @@ export default class Report401Generator extends ReportGenerator {
from: number,
to: number
): Promise<TaxReport401> {
const companyKYC = await getCompanyKYCByCompanyId(companyId);
const companyKYC: CompanyKYC | null = await getCompanyKYCByCompanyId(companyId);
if (!companyKYC) {
throw new Error(STATUS_MESSAGE.FORBIDDEN);
// Info: (20240912 - Murky) temporary allow to generate report without KYC
// throw new Error(STATUS_MESSAGE.FORBIDDEN);
}
const ROCStartDate = convertTimestampToROCDate(from);
const ROCEndDate = convertTimestampToROCDate(to);
// 1. 獲取所有發票
const journalList = await listJournalFor401(companyId, from, to);
const basicInfo = {
uniformNumber: companyKYC.registrationNumber,
businessName: companyKYC.legalName,
personInCharge: companyKYC.representativeName,
uniformNumber: companyKYC?.registrationNumber ?? '',
businessName: companyKYC?.legalName ?? '',
personInCharge: companyKYC?.representativeName ?? '',
taxSerialNumber: 'ABC123', // TODO (20240808 - Jacky): Implement this field in next sprint
businessAddress: companyKYC.address,
businessAddress: companyKYC?.address ?? '',
currentYear: ROCStartDate.year.toString(),
startMonth: ROCStartDate.month.toString(),
endMonth: ROCEndDate.month.toString(),
Expand Down
138 changes: 71 additions & 67 deletions src/lib/utils/type_guard/company_kyc.ts
Original file line number Diff line number Diff line change
@@ -1,80 +1,84 @@
import {
BasicInfoKeys,
ContactInfoKeys,
CountryOptions,
IndustryOptions,
LegalStructureOptions,
RegistrationInfoKeys,
RepresentativeIDType,
UploadDocumentKeys,
} from '@/constants/kyc';
import { ICompanyKYC, ICompanyKYCForm } from '@/interfaces/company_kyc';
import { getEnumValue } from '@/lib/utils/common';
import { CompanyKYC } from '@prisma/client';
import { iCompanyKYCFormValidator, iCompanyKYCValidator } from '@/lib/utils/zod_schema/kyc';

export function isCompanyKYC(data: CompanyKYC): data is ICompanyKYC {
return (
typeof data.id === 'number' &&
typeof data.companyId === 'number' &&
typeof data.legalName === 'string' &&
Object.values(CountryOptions).includes(data.country as CountryOptions) &&
typeof data.city === 'string' &&
typeof data.address === 'string' &&
typeof data.zipCode === 'string' &&
typeof data.representativeName === 'string' &&
Object.values(LegalStructureOptions).includes(data.structure as LegalStructureOptions) &&
typeof data.registrationNumber === 'string' &&
typeof data.registrationDate === 'string' &&
Object.values(IndustryOptions).includes(data.industry as IndustryOptions) &&
typeof data.contactPerson === 'string' &&
typeof data.contactPhone === 'string' &&
typeof data.contactEmail === 'string' &&
// (typeof data.website === 'string' || data.website === undefined) && Info: (20240719 - Tzuhan) this field should be optional, but db schema is not nullable
typeof data.website === 'string' &&
Object.values(RepresentativeIDType).includes(
data.representativeIdType as RepresentativeIDType
) &&
typeof data.registrationCertificateFileId === 'number' &&
typeof data.taxCertificateFileId === 'number' &&
typeof data.representativeIdCardFileId === 'number' &&
typeof data.createdAt === 'number' &&
typeof data.updatedAt === 'number' &&
(typeof data.deletedAt === 'number' || data.deletedAt === null)
);
export function isCompanyKYC(data: unknown): data is ICompanyKYC {
// Deprecated: (20240912 - Murky) Use zod validator instead
// return (
// typeof data.id === 'number' &&
// typeof data.companyId === 'number' &&
// typeof data.legalName === 'string' &&
// Object.values(CountryOptions).includes(data.country as CountryOptions) &&
// typeof data.city === 'string' &&
// typeof data.address === 'string' &&
// typeof data.zipCode === 'string' &&
// typeof data.representativeName === 'string' &&
// Object.values(LegalStructureOptions).includes(data.structure as LegalStructureOptions) &&
// typeof data.registrationNumber === 'string' &&
// typeof data.registrationDate === 'string' &&
// Object.values(IndustryOptions).includes(data.industry as IndustryOptions) &&
// typeof data.contactPerson === 'string' &&
// typeof data.contactPhone === 'string' &&
// typeof data.contactEmail === 'string' &&
// // (typeof data.website === 'string' || data.website === undefined) && Info: (20240719 - Tzuhan) this field should be optional, but db schema is not nullable
// typeof data.website === 'string' &&
// Object.values(RepresentativeIDType).includes(
// data.representativeIdType as RepresentativeIDType
// ) &&
// typeof data.registrationCertificateFileId === 'number' &&
// typeof data.taxCertificateFileId === 'number' &&
// typeof data.representativeIdCardFileId === 'number' &&
// typeof data.createdAt === 'number' &&
// typeof data.updatedAt === 'number' &&
// (typeof data.deletedAt === 'number' || data.deletedAt === null)
// );

const isValid = iCompanyKYCValidator.safeParse(data);

return isValid.success;
}

export function isCompanyKYCForm(obj: ICompanyKYCForm): obj is ICompanyKYCForm {
const countryEnumValue = getEnumValue(CountryOptions, obj.country);
const structureEnumValue = getEnumValue(LegalStructureOptions, obj.structure);
const industryEnumValue = getEnumValue(IndustryOptions, obj.industry);
const representativeIdTypeEnumValue = getEnumValue(
RepresentativeIDType,
obj.representativeIdType
);
return (
typeof obj === 'object' &&
!!countryEnumValue &&
!!structureEnumValue &&
!!industryEnumValue &&
!!representativeIdTypeEnumValue &&
typeof obj[BasicInfoKeys.LEGAL_COMPANY_NAME] === 'string' &&
typeof obj[BasicInfoKeys.CITY] === 'string' &&
typeof obj[BasicInfoKeys.ZIP_CODE] === 'string' &&
typeof obj[BasicInfoKeys.ADDRESS] === 'string' &&
typeof obj[BasicInfoKeys.KEY_COMPANY_REPRESENTATIVES_NAME] === 'string' &&
typeof obj[RegistrationInfoKeys.LEGAL_STRUCTURE] === 'string' &&
typeof obj[RegistrationInfoKeys.BUSINESS_REGISTRATION_NUMBER] === 'string' &&
typeof obj[RegistrationInfoKeys.REGISTRATION_DATE] === 'string' &&
typeof obj[RegistrationInfoKeys.INDUSTRY] === 'string' &&
typeof obj[ContactInfoKeys.KEY_CONTACT_PERSON] === 'string' &&
typeof obj[ContactInfoKeys.CONTACT_PHONE] === 'string' &&
typeof obj[ContactInfoKeys.EMAIL_ADDRESS] === 'string' &&
typeof obj[ContactInfoKeys.COMPANY_WEBSITE] === 'string' &&
typeof obj[UploadDocumentKeys.REPRESENTATIVE_ID_TYPE] === 'string' &&
typeof obj.registrationCertificateFileId === 'number' &&
typeof obj.taxCertificateFileId === 'number' &&
typeof obj.representativeIdCardFileId === 'number'
);
export function isCompanyKYCForm(obj: unknown): obj is ICompanyKYCForm {
// Deprecated: (20240912 - Murky) Use zod validator instead
// const countryEnumValue = getEnumValue(CountryOptions, obj.country);
// const structureEnumValue = getEnumValue(LegalStructureOptions, obj.structure);
// const industryEnumValue = getEnumValue(IndustryOptions, obj.industry);
// const representativeIdTypeEnumValue = getEnumValue(
// RepresentativeIDType,
// obj.representativeIdType
// );
// return (
// typeof obj === 'object' &&
// !!countryEnumValue &&
// !!structureEnumValue &&
// !!industryEnumValue &&
// !!representativeIdTypeEnumValue &&
// typeof obj[BasicInfoKeys.LEGAL_COMPANY_NAME] === 'string' &&
// typeof obj[BasicInfoKeys.CITY] === 'string' &&
// typeof obj[BasicInfoKeys.ZIP_CODE] === 'string' &&
// typeof obj[BasicInfoKeys.ADDRESS] === 'string' &&
// typeof obj[BasicInfoKeys.KEY_COMPANY_REPRESENTATIVES_NAME] === 'string' &&
// typeof obj[RegistrationInfoKeys.LEGAL_STRUCTURE] === 'string' &&
// typeof obj[RegistrationInfoKeys.BUSINESS_REGISTRATION_NUMBER] === 'string' &&
// typeof obj[RegistrationInfoKeys.REGISTRATION_DATE] === 'string' &&
// typeof obj[RegistrationInfoKeys.INDUSTRY] === 'string' &&
// typeof obj[ContactInfoKeys.KEY_CONTACT_PERSON] === 'string' &&
// typeof obj[ContactInfoKeys.CONTACT_PHONE] === 'string' &&
// typeof obj[ContactInfoKeys.EMAIL_ADDRESS] === 'string' &&
// typeof obj[ContactInfoKeys.COMPANY_WEBSITE] === 'string' &&
// typeof obj[UploadDocumentKeys.REPRESENTATIVE_ID_TYPE] === 'string' &&
// typeof obj.registrationCertificateFileId === 'number' &&
// typeof obj.taxCertificateFileId === 'number' &&
// typeof obj.representativeIdCardFileId === 'number'
// );

const isValid = iCompanyKYCFormValidator.safeParse(obj);
return isValid.success;
}

export function isKYCFormComplete(data: ICompanyKYCForm): {
Expand Down
26 changes: 26 additions & 0 deletions src/lib/utils/zod_schema/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { z } from 'zod';
import { timestampInSeconds } from '@/lib/utils/common';

export const zodStringToNumber = z.string().regex(/^\d+$/).transform(Number);

export function zodStringToNumberWithDefault(defaultValue: number) {
return z
.string()
.regex(/^\d+$/)
.optional()
.transform((val) => (val ? Number(val) : defaultValue));
}

export function zodTimestampInSeconds(canBeUndefined: boolean = false) {
if (canBeUndefined) {
return z
.string()
.regex(/^\d+$/)
.optional()
.transform((val) => (val ? timestampInSeconds(Number(val)) : undefined));
}
return z
.string()
.regex(/^\d+$/)
.transform((val) => timestampInSeconds(Number(val)));
}
27 changes: 8 additions & 19 deletions src/lib/utils/zod_schema/invoice.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { EventType, PaymentPeriodType, PaymentStatusType } from '@/constants/account';
import { EventType } from '@/constants/account';
import { IZodValidator } from '@/interfaces/zod_validator';
import { z } from 'zod';
import { iPaymentValidator } from '@/lib/utils/zod_schema/payment';
import { zodStringToNumber } from '@/lib/utils/zod_schema/common';

const invoiceZod = z.object({
const iInvoiceValidator = z.object({
journalId: z.number().nullable(),
date: z.number(), // Info: (20240522 - Murky) timestamp
eventType: z.nativeEnum(EventType),
Expand All @@ -13,27 +15,14 @@ const invoiceZod = z.object({
project: z.string().nullable(),
contractId: z.number().nullable(),
contract: z.string().nullable(),
payment: z.object({
isRevenue: z.boolean(),
price: z.number(),
hasTax: z.boolean(),
taxPercentage: z.number(),
hasFee: z.boolean(),
fee: z.number(),
method: z.string(),
period: z.nativeEnum(PaymentPeriodType),
installmentPeriod: z.number(),
alreadyPaid: z.number(),
status: z.nativeEnum(PaymentStatusType),
progress: z.number(),
}),
payment: iPaymentValidator,
});

const invoiceCreateQueryValidator = z.object({});

const invoiceCreateBodyValidator = z.object({
ocrId: z.number().optional(),
invoice: invoiceZod,
invoice: iInvoiceValidator,
});

export const invoiceCreateValidator: IZodValidator<
Expand All @@ -47,7 +36,7 @@ export const invoiceCreateValidator: IZodValidator<

const invoiceUpdateQueryValidator = z.object({});
const invoiceUpdateBodyValidator = z.object({
invoice: invoiceZod,
invoice: iInvoiceValidator,
});

export const invoiceUpdateValidator: IZodValidator<
Expand All @@ -59,7 +48,7 @@ export const invoiceUpdateValidator: IZodValidator<
};

const invoiceGetByIdQueryValidator = z.object({
invoiceId: z.string().regex(/^\d+$/).transform(Number),
invoiceId: zodStringToNumber,
});

const invoiceGetByIdBodyValidator = z.object({});
Expand Down
Loading

0 comments on commit 09adc7e

Please sign in to comment.