Skip to content

Commit

Permalink
判定ロジック変更
Browse files Browse the repository at this point in the history
  • Loading branch information
gentksb committed Jul 21, 2024
1 parent 232e676 commit 7986add
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 46 deletions.
4 changes: 2 additions & 2 deletions lambda/spilitwise-automation/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
APIGatewayEvent,
Handler,
} from "aws-lambda";
import { splitRecent20Expenses } from "./main";
import { splitRecentExpenses } from "./main";

export const handler: Handler = async (
event: APIGatewayEvent,
Expand Down Expand Up @@ -33,7 +33,7 @@ export const handler: Handler = async (
throw new Error("環境変数が設定されていません");
}

const resuleMessage = await splitRecent20Expenses({
const resuleMessage = await splitRecentExpenses({
SPLITWISE_API_KEY_PARAMETER_NAME,
SLACK_WEBHOOK_URL,
USER1_ID,
Expand Down
34 changes: 26 additions & 8 deletions lambda/spilitwise-automation/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
import { splitExpense } from "./src/logic/splitExpense";
import { isExpenseEligibleForSplitting } from "./src/validator/isExpenseEligibleForSplitting";
import { isNeededReSplit } from "./src/validator/isNeededResplit";
import { components, paths } from "../../@types/splitwise";
import { IncomingWebhook } from "@slack/webhook";

Expand All @@ -16,7 +16,7 @@ interface Props {

type Expense = components["schemas"]["expense"];

export const splitRecent20Expenses = async (props: Props) => {
export const splitRecentExpenses = async (props: Props) => {
const {
SPLITWISE_API_KEY_PARAMETER_NAME,
SLACK_WEBHOOK_URL,
Expand All @@ -30,6 +30,7 @@ export const splitRecent20Expenses = async (props: Props) => {

const axios_option: AxiosRequestConfig = {
headers: { Authorization: `Bearer ${SPLITWISE_API_KEY_PARAMETER_NAME}` },
params: { limit: 100 },
};

axios.interceptors.response.use(
Expand All @@ -48,10 +49,10 @@ export const splitRecent20Expenses = async (props: Props) => {
axios_option
);

const expensesList: Expense[] = getExpenses.data.expenses || [];
const expenses: Expense[] = getExpenses.data.expenses || [];

// リストが空か0の場合は処理を終了
if (expensesList.length === 0) {
if (expenses.length === 0) {
return {
statusCode: 200,
body: JSON.stringify({
Expand All @@ -60,9 +61,26 @@ export const splitRecent20Expenses = async (props: Props) => {
};
}

const willSplitExpenses = expensesList.filter((expense) =>
isExpenseEligibleForSplitting({
const firstDayOfCurrenMonth = new Date(
new Date().getFullYear(),
new Date().getMonth(),
1
).toISOString();
// 最新の精算日を取得. expensesは支払い日の降順で取得されることが保証済み
// 精算日を取得できない場合は当月1日とする
const lastPaymentDate =
expenses.filter((expense) => expense.payment === true)[0].created_at ||
firstDayOfCurrenMonth;

// Todo: 判定ルールを個別に定義する
// isPayment => 精算レコード(個々の経費ではないレコード)判定
// isTargetGroup => 対象のグループIDかどうか
// isTargetSplitRate => 環境変数の割り勘率が設定されているか
// isAfterPayment => 最新の精算レコード以降のレコードかどうか
const willSplitExpenses = expenses.filter((expense) =>
isNeededReSplit({
expense,
lastPaymentDate,
USER1_RATE,
USER2_RATE,
SPLITWISE_GROUP_ID,
Expand Down Expand Up @@ -166,9 +184,9 @@ export const splitRecent20Expenses = async (props: Props) => {
);

const logMessage =
expensesList.length === 0
expenses.length === 0
? "取得対象の精算経費がありません"
: `直近${expensesList.length}の経費のうち、${willSplitExpenses.length}件を割り勘処理しました`;
: `直近${expenses.length}の経費のうち、${willSplitExpenses.length}件を割り勘処理しました`;
console.log(logMessage);

return { result: logMessage };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ type Expense = components["schemas"]["expense"];

interface Props {
expense: Expense;
lastPaymentDate: string;
USER1_RATE: string;
USER2_RATE: string;
SPLITWISE_GROUP_ID: string;
}

export const isExpenseEligibleForSplitting = ({
export const isNeededReSplit = ({
expense,
lastPaymentDate,
USER1_RATE,
USER2_RATE,
SPLITWISE_GROUP_ID,
Expand All @@ -34,7 +36,8 @@ export const isExpenseEligibleForSplitting = ({
expense.group_id === undefined ||
expense.users === undefined ||
expense.users.length < 2 ||
expense.cost === undefined
expense.cost === undefined ||
expense.created_at === undefined
) {
console.error("割り勘費用の情報に不備があります", expense);
return false;
Expand All @@ -44,24 +47,18 @@ export const isExpenseEligibleForSplitting = ({
const splitRate = parseFloat(
(parseInt(users?.[0]?.owed_share ?? "0") / parseInt(cost)).toPrecision(2)
);
const isCurrentMonth = (created_at: string | undefined) => {
if (created_at) {
return new Date(created_at).getMonth() === new Date().getMonth();
}
console.error("created_atが不正です", created_at);
return false;
};

// グループIDが一致し、割り勘でない、かつ、割り勘率が0,1,USER1_RATE,USER2_RATE以外の場合は処理対象とする
// payment:true -> 精算レコード(個々の支払い終了ではない)
// 一度いじったものを再度いじらないようにするため、payment以前の対象は除外したいが判定が難しいので今月内のものだけを対象とする
return (
expense.payment === false &&
expense.group_id?.toString() === SPLITWISE_GROUP_ID &&
const isPayment = expense.payment === true;
const isTargetGroup = expense.group_id?.toString() === SPLITWISE_GROUP_ID;
const isAfterPayment = new Date(created_at) >= new Date(lastPaymentDate);
const isTargetSplitRate =
splitRate !== 0 &&
splitRate !== 1 &&
splitRate !== parseFloat(USER1_RATE) &&
splitRate !== parseFloat(USER2_RATE) &&
isCurrentMonth(created_at)
);
splitRate !== parseFloat(USER2_RATE);

// グループIDが一致し、割り勘でない、かつ、割り勘率が0,1,USER1_RATE,USER2_RATE以外の場合は処理対象とする
// payment:true -> 精算レコード(個々の支払い終了ではない)
// 一度いじったものを再度いじらないようにするため、payment以前の対象は除外したいが判定が難しいので今月内のものだけを対象とする
return !isPayment && isTargetGroup && isTargetSplitRate && isAfterPayment;
};
35 changes: 17 additions & 18 deletions test/splitExpense.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isExpenseEligibleForSplitting } from "../lambda/spilitwise-automation/src/validator/isExpenseEligibleForSplitting";
import { isNeededReSplit } from "../lambda/spilitwise-automation/src/validator/isNeededResplit";
import { splitExpense } from "../lambda/spilitwise-automation/src/logic/splitExpense";
import { components } from "../@types/splitwise";

Expand All @@ -15,12 +15,17 @@ if (
throw new Error("環境変数が不足しています");
}

const firstDayOfCurrenMonth = new Date(
new Date().getFullYear(),
new Date().getMonth(),
1
).toISOString();

// 環境変数を設定済みの関数
const isExpenseEligibleForSplittingWrapper = (
expense: components["schemas"]["expense"]
) =>
isExpenseEligibleForSplitting({
const isNeededReSplitWrapper = (expense: components["schemas"]["expense"]) =>
isNeededReSplit({
expense,
lastPaymentDate: firstDayOfCurrenMonth,
USER1_RATE,
USER2_RATE,
SPLITWISE_GROUP_ID,
Expand All @@ -47,15 +52,13 @@ describe("異常系テスト", () => {
};

// console.errorの出力をAssertする
expect(isExpenseEligibleForSplittingWrapper(missingGroupIdData)).toBe(
false
);
expect(isNeededReSplitWrapper(missingGroupIdData)).toBe(false);
});
});

describe("補正対象判定処理テスト", () => {
test("典型例: 支払い前でデフォルト負担率(50:50)のデータを処理する", () => {
expect(isExpenseEligibleForSplittingWrapper(basicExpense)).toBeTruthy();
expect(isNeededReSplitWrapper(basicExpense)).toBeTruthy();
});

test("100%負担のデータは処理対象としない", () => {
Expand All @@ -77,7 +80,7 @@ describe("補正対象判定処理テスト", () => {
},
],
};
expect(isExpenseEligibleForSplittingWrapper(simpleDebtExpense)).toBeFalsy();
expect(isNeededReSplitWrapper(simpleDebtExpense)).toBeFalsy();
});

test("補正済みデータは処理対象としない", () => {
Expand All @@ -99,27 +102,23 @@ describe("補正対象判定処理テスト", () => {
},
],
};
expect(isExpenseEligibleForSplittingWrapper(reSplittedExpense)).toBeFalsy();
expect(isNeededReSplitWrapper(reSplittedExpense)).toBeFalsy();
});

test("指定したグループID以外は処理対象としない", () => {
const nonTargetGroupExpense: components["schemas"]["expense"] = {
...basicExpense,
group_id: 88888888,
};
expect(
isExpenseEligibleForSplittingWrapper(nonTargetGroupExpense)
).toBeFalsy();
expect(isNeededReSplitWrapper(nonTargetGroupExpense)).toBeFalsy();
});

test("前月のデータは対象としない", () => {
test("Payment Date以前のデータは対象としない", () => {
const nonTargetGroupExpense: components["schemas"]["expense"] = {
...basicExpense,
created_at: "2021-08-31T00:00:00Z",
};
expect(
isExpenseEligibleForSplittingWrapper(nonTargetGroupExpense)
).toBeFalsy();
expect(isNeededReSplitWrapper(nonTargetGroupExpense)).toBeFalsy();
});
});

Expand Down

0 comments on commit 7986add

Please sign in to comment.