feat : add monthly
CI / Verify (push) Successful in 2m50s
CI / Build immutable images and deploy (push) Successful in 2m26s

This commit is contained in:
2026-10-04 15:29:02 +07:00 Unverified
parent 33ad2f498b
commit 643e77a88f
14 changed files with 4245 additions and 28 deletions
+25
View File
@@ -0,0 +1,25 @@
import { describe, expect, it } from "vitest";
import { commissionIncomeDay, commissionIncomeMonth, commissionIncomeShares } from "./income";
describe("commission income periods", () => {
it("uses Bangkok month boundaries and rolls across years", () => {
const december = commissionIncomeMonth("2026-12");
expect(december.from.toISOString()).toBe("2026-11-30T17:00:00.000Z");
expect(december.to.toISOString()).toBe("2026-12-31T17:00:00.000Z");
expect(december.next).toBe("2027-01");
expect(commissionIncomeMonth("2027-01").previous).toBe("2026-12");
});
it("defaults to the current Bangkok month for an invalid query", () => {
expect(commissionIncomeMonth("2026-13", new Date("2026-09-30T18:00:00Z")).month).toBe("2026-10");
});
it("assigns payments around midnight to the correct Bangkok day", () => {
expect(commissionIncomeDay(new Date("2026-10-01T16:59:59Z"))).toBe(1);
expect(commissionIncomeDay(new Date("2026-10-01T17:00:00Z"))).toBe(2);
});
});
it("calculates Buzz and Gunshin shares in satang", () => {
expect(commissionIncomeShares(101)).toEqual({ buzzSatang: 9090, gunshinSatang: 1010 });
});
+29
View File
@@ -0,0 +1,29 @@
const BANGKOK_OFFSET = "+07:00";
export function commissionIncomeMonth(value: string | string[] | undefined, now = new Date()) {
const currentParts = new Intl.DateTimeFormat("en-US", {
timeZone: "Asia/Bangkok", year: "numeric", month: "2-digit",
}).formatToParts(now);
const current = `${currentParts.find((part) => part.type === "year")!.value}-${currentParts.find((part) => part.type === "month")!.value}`;
const month = typeof value === "string" && /^(20\d{2}|[3-9]\d{3})-(0[1-9]|1[0-2])$/.test(value) ? value : current;
const [year, number] = month.split("-").map(Number);
const next = number === 12 ? `${year + 1}-01` : `${year}-${String(number + 1).padStart(2, "0")}`;
const previous = number === 1 ? `${year - 1}-12` : `${year}-${String(number - 1).padStart(2, "0")}`;
return {
month, year, number, previous, next,
from: new Date(`${month}-01T00:00:00${BANGKOK_OFFSET}`),
to: new Date(`${next}-01T00:00:00${BANGKOK_OFFSET}`),
days: new Date(Date.UTC(year, number, 0)).getUTCDate(),
leadingDays: (new Date(Date.UTC(year, number - 1, 1)).getUTCDay() + 6) % 7,
};
}
export function commissionIncomeDay(date: Date) {
return Number(new Intl.DateTimeFormat("en-US", {
timeZone: "Asia/Bangkok", day: "numeric",
}).format(date));
}
export function commissionIncomeShares(amountBaht: number) {
return { buzzSatang: amountBaht * 90, gunshinSatang: amountBaht * 10 };
}