Skip to content

Commit

Permalink
refactor: 💡 export-ledger API with the same utils as list-ledge
Browse files Browse the repository at this point in the history
-r

✅ Closes: #3743
  • Loading branch information
arealclimber committed Dec 24, 2024
1 parent 24b20a2 commit 5cb5bfe
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 49 deletions.
54 changes: 31 additions & 23 deletions src/lib/utils/ledger.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { CurrencyType } from '@/constants/currency';
import { LabelType } from '@/constants/ledger';
import { STATUS_MESSAGE } from '@/constants/status_code';
import { IAccountBookLedgerJSON } from '@/interfaces/account_book_node';
import { getAllLineItemsInPrisma } from '@/lib/utils/repo/line_item.repo';
import { ILedgerItem, ILedgerTotal } from '@/interfaces/ledger';
import { getLedgerJSON } from '@/lib/utils/repo/account_book.repo';
import { getAccountingSettingByCompanyId } from '@/lib/utils/repo/accounting_setting.repo';
import { EventType, EVENT_TYPE_TO_VOUCHER_TYPE_MAP } from '@/constants/account';
import { ILineItemSimpleAccountVoucher } from '@/interfaces/line_item';

Expand Down Expand Up @@ -90,24 +87,35 @@ export const convertLedgerJsonToCsvData = (
return csvData;
};

/**
* 驗證分頁參數是否有效
*/
export const validatePagination = (pageNumber: number): void => {
if (pageNumber < 1) {
throw new Error(STATUS_MESSAGE.INVALID_INPUT_PARAMETER);
}
};

/**
* 獲取會計幣別設定
*/
export const fetchCurrencyAlias = async (companyId: number): Promise<CurrencyType> => {
const accountingSettingData = await getAccountingSettingByCompanyId(companyId);
return (accountingSettingData?.currency as CurrencyType) || CurrencyType.TWD;
export const convertLedgerItemToCsvData = (
ledgerItems: ILedgerItem[],
voucherMap: Map<
number,
{
id: number;
date: string;
no: string;
type: string;
}
>
) => {
const csvData = ledgerItems.map((item) => {
return {
accountId: item.accountId,
no: item.no,
accountingTitle: item.accountingTitle,
voucherNumber: voucherMap.get(item.voucherId)?.no,
voucherDate: voucherMap.get(item.voucherId)?.date,
particulars: item.particulars,
debitAmount: item.debitAmount,
creditAmount: item.creditAmount,
balance: item.balance,
};
});
return csvData;
};

/**
/** Info: (20241224 - Shirley)
* 獲取分錄明細
*/
export const fetchLineItems = async (
Expand All @@ -119,7 +127,7 @@ export const fetchLineItems = async (
return rs;
};

/**
/** Info: (20241224 - Shirley)
* 根據科目範圍過濾分錄
*/
export const filterByAccountRange = (
Expand All @@ -142,7 +150,7 @@ export const filterByAccountRange = (
});
};

/**
/** Info: (20241224 - Shirley)
* 根據標籤類型過濾分錄
*/
export const filterByLabelType = (
Expand All @@ -162,7 +170,7 @@ export const filterByLabelType = (
});
};

/**
/** Info: (20241224 - Shirley)
* 排序並計算餘額變化
*/
export const sortAndCalculateBalances = (
Expand Down Expand Up @@ -208,7 +216,7 @@ export const sortAndCalculateBalances = (
});
};

/**
/** Info: (20241224 - Shirley)
* 計算總額
*/
export const calculateTotals = (processedLineItems: ILedgerItem[]): ILedgerTotal => {
Expand Down
52 changes: 31 additions & 21 deletions src/pages/api/v2/company/[companyId]/ledger/export/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,21 @@ import { APIName } from '@/constants/api_connection';
import { loggerError } from '@/lib/utils/logger_back';
import { formatApiResponse, formatTimestampByTZ } from '@/lib/utils/common';
import {
convertLedgerJsonToCsvData,
filterLedgerJSONByLabelType,
getLedgerFromAccountBook,
convertLedgerItemToCsvData,
fetchLineItems,
filterByAccountRange,
filterByLabelType,
sortAndCalculateBalances,
} from '@/lib/utils/ledger';
import { DEFAULT_TIMEZONE } from '@/constants/common';
import { ledgerAvailableFields, LedgerFieldsMap } from '@/constants/export_ledger';
import { findVouchersByVoucherIds } from '@/lib/utils/repo/voucher.repo';
import { LabelType } from '@/constants/ledger';

const handlePostRequest = async (req: NextApiRequest, res: NextApiResponse) => {
const { fileType, filters, options } = req.body;
const { companyId } = req.query;
const { startDate, endDate, labelType } = filters;
const { startDate, endDate, labelType, startAccountNo, endAccountNo } = filters;

if (!companyId) {
throw new Error(STATUS_MESSAGE.INVALID_COMPANY_ID);
Expand All @@ -37,29 +40,36 @@ const handlePostRequest = async (req: NextApiRequest, res: NextApiResponse) => {
throw new Error(STATUS_MESSAGE.INVALID_FILE_TYPE);
}

const rawLedgerJSON = await getLedgerFromAccountBook(+companyId, +startDate, +endDate);
const ledgerJSON = filterLedgerJSONByLabelType(rawLedgerJSON, labelType);
try {
let lineItems = await fetchLineItems(+companyId, +startDate, +endDate);
lineItems = filterByAccountRange(lineItems, startAccountNo, endAccountNo);
lineItems = filterByLabelType(lineItems, labelType as LabelType);
const processedLineItems = sortAndCalculateBalances(lineItems);

const voucherIds = ledgerJSON.map((ledger) => ledger.voucherId);
const vouchers = await findVouchersByVoucherIds(voucherIds);
const vouchersWithTz = vouchers.map((voucher) => ({
...voucher,
date: formatTimestampByTZ(voucher.date, options?.timezone || DEFAULT_TIMEZONE, 'YYYY-MM-DD'),
}));
const vouchersMap = new Map(vouchersWithTz.map((voucher) => [voucher.id, voucher]));
const voucherIds = processedLineItems.map((ledger) => ledger.voucherId);
const vouchers = await findVouchersByVoucherIds(voucherIds);
const vouchersWithTz = vouchers.map((voucher) => ({
...voucher,
date: formatTimestampByTZ(voucher.date, options?.timezone || DEFAULT_TIMEZONE, 'YYYY-MM-DD'),
}));
const vouchersMap = new Map(vouchersWithTz.map((voucher) => [voucher.id, voucher]));

const ledgerCsvData = convertLedgerJsonToCsvData(ledgerJSON, vouchersMap);
const ledgerCsvData = convertLedgerItemToCsvData(processedLineItems, vouchersMap);

const data = ledgerCsvData;
const data = ledgerCsvData;

// Info: (20241212 - Shirley) 處理欄位選擇
const fields = options?.fields || ledgerAvailableFields;
// Info: (20241212 - Shirley) 處理欄位選擇
const fields = options?.fields || ledgerAvailableFields;

const csv = convertToCSV(fields, data, LedgerFieldsMap);
const csv = convertToCSV(fields, data, LedgerFieldsMap);

res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Disposition', `attachment; filename=ledger_${Date.now()}.csv`);
res.send(csv);
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Disposition', `attachment; filename=ledger_${Date.now()}.csv`);
res.send(csv);
} catch (error) {
const err = error as Error;
throw new Error(err.message);
}
};

const methodHandlers: {
Expand Down
16 changes: 11 additions & 5 deletions src/pages/api/v2/company/[companyId]/ledger/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import { withRequestValidation } from '@/lib/utils/middleware';
import { APIName } from '@/constants/api_connection';
import { IHandleRequest } from '@/interfaces/handleRequest';
import { formatPaginatedLedger } from '@/lib/utils/formatter/ledger.formatter';
import { getAccountingSettingByCompanyId } from '@/lib/utils/repo/accounting_setting.repo';
import { LabelType } from '@/constants/ledger';
import {
calculateTotals,
filterByAccountRange,
fetchLineItems,
fetchCurrencyAlias,
filterByLabelType,
sortAndCalculateBalances,
validatePagination,
} from '@/lib/utils/ledger';
import { CurrencyType } from '@/constants/currency';

interface IPayload extends ILedgerPayload {}

Expand Down Expand Up @@ -55,11 +55,17 @@ export const handleGetRequest: IHandleRequest<APIName.LEDGER_LIST, IPayload> = a
const pageNumber = page;

try {
validatePagination(pageNumber);
if (pageNumber < 1) {
throw new Error(STATUS_MESSAGE.INVALID_INPUT_PARAMETER);
}

const currencyAlias = await fetchCurrencyAlias(companyId);
let lineItems = await fetchLineItems(companyId, startDate, endDate);
let currencyAlias = CurrencyType.TWD;
const accountingSettingData = await getAccountingSettingByCompanyId(companyId);
if (accountingSettingData?.currency) {
currencyAlias = accountingSettingData.currency as CurrencyType;
}

let lineItems = await fetchLineItems(companyId, startDate, endDate);
lineItems = filterByAccountRange(lineItems, startAccountNo, endAccountNo);
lineItems = filterByLabelType(lineItems, labelType as LabelType);
const processedLineItems = sortAndCalculateBalances(lineItems);
Expand Down

0 comments on commit 5cb5bfe

Please sign in to comment.