Tran Manh Tuan

TaxPal

TaxPal là nền tảng môi giới dịch vụ kế toán tại Nhật Bản, kết nối ba phía: khách hàng cần kế toán viên hoặc chuyên gia, đội sales / môi giới tiếp nhận và theo đến cùng từng yêu cầu, và kế toán viên / chuyên gia cung cấp dịch vụ. Mỗi phía có khu vực riêng trong sản phẩm, nhưng cùng dùng chung dữ liệu company, case và lịch sử cung cấp dịch vụ.

Năm 2022, tôi là Tech Lead của team tại Việt Nam và điều phối phía Việt Nam trong giai đoạn phát triển chính. Phạm vi của team là Sales Dashboard: hệ thống analytics nội bộ mà đội môi giới và quản lý dùng để theo dõi case, KPI, hiệu suất sales và tỷ lệ chuyển đổi — theo từng nhân viên, từng team và toàn công ty.

Vì sao đây là dự án hệ thống quản lý: phần khó chưa bao giờ là vẽ biểu đồ, mà là quyết định một con số có nghĩa là gì — case nào được đếm, theo ngày làm việc nào, trong kỳ quyết toán nào, với nhân viên đã nghỉ thì sao — rồi trả những con số đó về thật nhanh. Đây cũng là dự án brownfield: xây dashboard mới trên codebase AWS có sẵn, cùng GraphQL resolver, database, authentication và quy trình release của hệ thống cũ.

Nền tảng và người dùng

Khách hàng mang nhu cầu tới. Đội sales / môi giới biến nhu cầu đó thành case, follow và đưa case tới hợp đồng; quản lý theo dõi hiệu suất của từng người, từng team và toàn công ty. Kế toán viên / chuyên gia nhận các case tới lượt mình và có dashboard riêng.

Hệ sinh thái TaxPalKhách hàng cần dịch vụ đi vào khu vực khách hàng, nơi tạo ra dữ liệu company, customer và case. Case được chuyển tới đội Sales / môi giới làm việc trong khu vực Sales; khu vực này cập nhật lại case và dẫn tới Sales Dashboard. Case cũng được chuyển tới kế toán viên / chuyên gia, những người dùng khu vực Accountant / Expert và Expert Dashboard.

Khách hàng cần dịch vụ

Đội Sales / môi giới

Kế toán viên / Chuyên gia

Khu vực khách hàng

Khu vực Sales / môi giới

Khu vực Accountant / Expert

Company / Customer / Case

Sales Dashboard

Expert Dashboard

Sơ đồ vừa khung

Ba nhóm người dùng, một bộ dữ liệu company và case dùng chung. Sales Dashboard nằm ở phía môi giới của sản phẩm.

Vai trò của tôi

  • Theo dõi và kiểm soát tiến độ kỹ thuật, schedule và workload của team theo từng menu / milestone.
  • Điều phối giữa Backend, Frontend và IT Communicator; làm việc định kỳ với khách hàng Nhật Bản.
  • Phân tích yêu cầu nghiệp vụ thành thiết kế kỹ thuật; breakdown task và estimate.
  • Review API design và pull request.
  • Hỗ trợ xử lý các vấn đề khó về GraphQL, Prisma, database và integration; điều phối sửa bug giữa FE và BE.
  • Đánh giá rủi ro ảnh hưởng tiến độ, làm rõ với khách hàng các điểm chưa rõ trong tài liệu nghiệp vụ.
  • Quản lý bàn giao và release theo quy trình repository của khách hàng; tổng hợp tình trạng, issue, nguyên nhân và phương án xử lý để báo cáo.

Từ yêu cầu tới hợp đồng: pipeline của case

Mỗi nhu cầu của khách hàng là một case, gắn với dữ liệu company — dữ liệu master mà lịch hẹn, tiến độ và hợp đồng đều liên kết vào. Saler có thể được chỉ định thủ công — hệ thống không ép tổ chức theo một thuật toán auto-assign cứng. Từ đó case đi qua nhiều trạng thái sales: khả năng cao lấy được lịch hẹn, đang có vấn đề cần giải quyết, khó hoặc không khả thi, đang liên hệ nhưng chưa kết nối được, đã xác nhận hoặc bị cancel lịch hẹn, đang follow, đã lên hợp đồng, đã lên hợp đồng kế toán, tạm dừng, hủy hợp đồng, hủy khi renew. Ở phiên bản nghiệp vụ được mô tả, saler tự chọn status trên màn hình case thay vì một state machine tự động hoàn toàn.

Luồng xử lý một caseKhách hàng phát sinh nhu cầu, hệ thống tạo hoặc tìm company, tạo case, assign saler, rồi saler liên hệ khách hàng. Chưa kết nối được thì retry và liên hệ lại. Kết nối được thì tiếp tục follow và xét khả năng đặt lịch: không khả thi thì dừng và đóng case; có khả năng thì xác nhận appointment, meeting và follow-up. Nếu tiến tới hợp đồng thì lập contract rồi accounting contract / service; nếu không thì cancel / close. Đây là bản đơn giản hóa từ hệ thống status thực tế.

Không

Có

Không

Có

Có

Không

Khách hàng phát sinh nhu cầu

Tạo / tìm Company

Tạo Case

Assign Saler

Liên hệ khách hàng

Kết nối được?

Tiếp tục follow

Chưa kết nối / retry

Có khả năng đặt lịch?

Xác nhận Appointment

Không khả thi / dừng

Meeting / Follow-up

Tiến tới hợp đồng?

Contract

Accounting Contract / Service

Cancel / Close

Sơ đồ vừa khung

Bản đơn giản hóa từ hệ thống status thực tế. Case — không phải khách hàng — là đơn vị để tính mọi số lượng, KPI và tỷ lệ chuyển đổi.

Sales Dashboard hiển thị những gì

Dashboard biến case thành các góc nhìn quản trị: case mới và đang xử lý, số lượng tiếp nhận, KPI, kết quả thực tế, sales, tỷ lệ thành công, hiệu suất theo từng người, từng team và toàn công ty. Trong đó có bảng case theo tuần cho từng thành viên và team, KPI theo ngày / tháng / năm, sales ranking, doanh số theo dịch vụ trong 4 năm gần nhất, và tỷ lệ thành công của pipeline trong khoảng từ hai tuần tới một năm.

Luồng dữ liệu của Sales DashboardSáu nguồn dữ liệu — case và company, staff master, team master, KPI / sales target, ngày làm việc / ngày nghỉ và sales status — cùng đi vào GraphQL Analytics API. API chuyển dữ liệu qua lớp aggregation và business rule, tạo ba góc nhìn: thống kê theo staff, theo team và toàn công ty. Thống kê theo staff hiển thị thành bảng và sales ranking; thống kê theo team và toàn công ty hiển thị thành biểu đồ.

Case / Company Data

Staff Master

Team Master

KPI / Sales Target

Working Day / Holiday

Sales Status

GraphQL Analytics API

Aggregation / Business Rules

Thống kê theo Staff

Thống kê theo Team

Thống kê toàn công ty

Tables

Charts

Sales Ranking

Sơ đồ vừa khung

Sáu nguồn dữ liệu, một analytics API, một lớp business rule, ba cấp tổng hợp.

KPI target so với actual

Quản lý nhập sales target theo năm, tháng, nhân viên và loại chỉ tiêu. Kỳ chưa có dữ liệu mở ra ở trạng thái trống để nhập mới; kỳ đã có dữ liệu mở ra để chỉnh sửa. Target sau đó được so với kết quả thực tế theo ngày, tháng và năm, để thấy được chênh lệch và xu hướng chứ không chỉ con số tổng.

KPI target so với actualManager hoặc người dùng của đội Sales nhập KPI target, lưu thành dữ liệu sales target. Song song, dữ liệu case, sales và contract được dùng để tính actual. Target và actual gặp nhau ở bước so sánh target với actual, rồi hiển thị theo ba mức thời gian: ngày, tháng và năm.

Manager / người dùng Sales

Nhập KPI Target

Sales Target Data

Case / Sales / Contract Data

Tính Actual

Target vs Actual

Daily

Monthly

Yearly

Sơ đồ vừa khung

Target nhập một lần; actual lấy từ chính dữ liệu case, sales và hợp đồng mà cả nền tảng đang dùng.

Đếm một khách hàng một lần: rule xử lý case trùng

Cùng một khách hàng có thể tới nhiều lần, qua các kênh liên hệ khác nhau hoặc vào các thời điểm khác nhau. Rule gom các case có cùng người đại diện, email hoặc số điện thoại phù hợp, và ngày tiếp nhận nằm trong một khoảng quy định. Nhóm có thể là chuỗi: A trùng B, B trùng C thì cả ba thuộc cùng một nhóm. Với mỗi nhóm, dashboard chọn một record đại diện và một giá trị forecast, để số lượng mail, contact và case không bị tăng sai.

Rule xử lý case trùngHệ thống lấy các case có khả năng liên quan rồi kiểm tra lần lượt ba điều kiện: cùng thông tin người đại diện, email hoặc điện thoại phù hợp, và ngày tiếp nhận nằm trong khoảng của rule. Case trượt bất kỳ điều kiện nào được xử lý như case độc lập. Case qua cả ba điều kiện được đưa vào duplicate group; hệ thống kiểm tra quan hệ chuỗi A-B-C, chọn record đại diện, chọn dữ liệu forecast theo rule, rồi đếm cả nhóm như một case trong thống kê.

Không

Có

Không

Có

Không

Có

Lấy các Case có khả năng liên quan

Cùng thông tin đại diện?

Email / điện thoại phù hợp?

Ngày tiếp nhận nằm trong rule?

Đưa vào Duplicate Group

Kiểm tra quan hệ chuỗi A-B-C

Chọn record đại diện

Chọn dữ liệu forecast theo rule

Count nhóm như một Case trong thống kê

Xử lý như Case độc lập

Sơ đồ vừa khung

Con số trên dashboard chỉ đúng khi rule đếm đúng. Case trùng được xử lý trước khi cộng bất kỳ thứ gì.

Con số phải đúng theo lịch và theo tổ chức

  • Bảng analytics tối ưu cho việc đọc. Để không phải tính lại toàn bộ từ các bảng giao dịch mỗi lần mở dashboard, bảng thống kê lưu sẵn những gì aggregation cần: kỳ tính toán cùng ngày bắt đầu / kết thúc, thứ tự team, loại thống kê, trạng thái nhân viên, ngày làm việc. Đọc nhanh và query đơn giản hơn; đánh đổi là khi business rule thay đổi có thể phải rebuild lại dữ liệu đó.
  • Ngày làm việc. Số ngày làm việc còn lại và ngày tiếp nhận hợp lệ được tính theo dữ liệu ngày nghỉ, không theo lịch thông thường.
  • Kỳ quyết toán. Năm chọn trên màn hình có thể là năm tài chính chạy từ tháng 10 tới tháng 9, nên frontend và backend phải thống nhất date range thay vì lọc theo năm dương lịch.
  • Nhân viên đã nghỉ. Cờ nghỉ việc nằm ngay trong dữ liệu thống kê, để báo cáo lịch sử vẫn giữ nhân viên cũ mà không xử lý họ như nhân viên đang làm trong mọi phép tính.

Expert Dashboard

Tài liệu dự án còn bao gồm dashboard dành cho chính kế toán viên / chuyên gia: thu nhập, hiệu quả kinh doanh, chiến lược, nhân sự, thừa kế, mức độ ứng dụng CNTT và sự hài lòng khách hàng, so sánh trên radar chart với các mốc cá nhân, nhóm, công ty và giá trị trung bình tham chiếu.

Cấu trúc Expert DashboardExpert Dashboard có bảy nhóm màn hình: thu nhập, hiệu quả kinh doanh, chiến lược, nhân sự, thừa kế, mức độ ứng dụng CNTT và sự hài lòng khách hàng. Cả bảy nhóm cùng đổ về radar chart và benchmark để so sánh nhiều chiều. Trang tổng quan còn dẫn tới phần search / reference.

Expert Dashboard

Thu nhập

Hiệu quả kinh doanh

Chiến lược

Nhân sự

Thừa kế

Mức độ ứng dụng CNTT

Sự hài lòng khách hàng

Radar Chart / Benchmark

Search / Reference

Sơ đồ vừa khung

Analytics không chỉ dành cho đội sales nội bộ: chuyên gia có góc nhìn riêng trên bảy chiều hoạt động.

Kiến trúc

Frontend không bao giờ truy cập database trực tiếp. Next.js và React gọi AWS AppSync qua Apollo; schema GraphQL được phân quyền bằng Cognito User Pools và IAM; mỗi request đi qua resolver, use case và repository, rồi Prisma truy cập PostgreSQL trên AWS. Codebase còn có Elasticsearch / OpenSearch cho search và indexing, và hạ tầng AWS được định nghĩa bằng Terraform.

Kiến trúc kỹ thuật mức high-level của TaxPalTrình duyệt chạy ứng dụng Next.js, React và TypeScript; ứng dụng gọi Apollo GraphQL client, client gọi AWS AppSync GraphQL. Cognito và IAM cấp quyền cho AppSync. AppSync chuyển request qua resolver layer, use case / interactor và repository. Repository đọc ghi PostgreSQL trên AWS qua Prisma, và làm việc với Elasticsearch / OpenSearch. Terraform định nghĩa hạ tầng AWS cho AppSync, Cognito / IAM, PostgreSQL và search. Đây là sơ đồ mức portfolio dựa trên các thành phần trong codebase và tài liệu, không phải bản sao sơ đồ hạ tầng production.

Browser

Next.js / React / TypeScript

Apollo / GraphQL Client

AWS AppSync GraphQL

Cognito / IAM

Resolver Layer

Use Case / Interactor

Repository

Prisma

PostgreSQL on AWS

Elasticsearch / OpenSearch

Terraform / AWS Infrastructure

Sơ đồ vừa khung

Sơ đồ mức portfolio dựng từ các thành phần trong codebase và tài liệu — không phải bản sao sơ đồ hạ tầng production.

Thêm một API luôn đi cùng một con đường: schema, code generation, resolver, use case, repository, Prisma query, rồi test và seed data. Pull request được phía khách hàng review và phải pass toàn bộ GitHub checks. Migration và seed data tách thành pull request riêng, để một thay đổi schema không bao giờ bị merge lẫn với code nghiệp vụ, vốn khó review và khó rollback.

Quy trình phát triển một GraphQL APITừ yêu cầu màn hình hoặc nghiệp vụ, team thiết kế API, viết GraphQL schema, chạy GraphQL codegen, rồi viết resolver, use case / interactor, repository và Prisma query tới PostgreSQL. Sau đó chuẩn bị test và seed data, rồi mở pull request.

Yêu cầu màn hình / nghiệp vụ

API Design

GraphQL Schema

GraphQL Codegen

Resolver

Use Case / Interactor

Repository

Prisma Query

PostgreSQL

Test / Seed Data

Pull Request

Sơ đồ vừa khung

Một con đường cho mọi API mới, từ yêu cầu tới pull request.

Tối ưu tốc độ dashboard

Dashboard có nhiều widget và nhiều phép thống kê nên hiệu năng là chuyện hằng ngày. Các vấn đề ghi nhận rất cụ thể: API select nhiều field hơn màn hình cần, load cả relation trong khi chỉ cần vài field, kiểm tra dữ liệu bằng nhiều request lặp, một màn hình chỉ khoảng 120 record mà API mất hơn 1 giây, và có dashboard gọi API riêng cho từng team. Cách xử lý: chỉ select dữ liệu cần, dùng relation thay cho query lặp, giảm kích thước response, trả dữ liệu nhiều team trong một API, giảm số request từ frontend, lazy load các widget người dùng chưa xem tới — kèm loading state để phân biệt "đang tải" với "không có dữ liệu".

Tối ưu API cho dashboardMột dashboard widget có hai cách lấy dữ liệu. Cách chưa tối ưu gửi nhiều request, mỗi team một request, select quá nhiều field và relation, nên response chậm. Cách tối ưu chỉ select dữ liệu cần, giảm query lặp bằng relation, aggregate nhiều team trong một API và lazy load widget, cho kết quả ít request hơn, payload nhỏ hơn và load tốt hơn.

Dashboard Widget

Cách chưa tối ưu

Nhiều request / team

Select nhiều field / relation

Response chậm

Thiết kế tối ưu

Chỉ select dữ liệu cần

Giảm query lặp bằng relation

Aggregate nhiều team trong một API

Lazy load widget

Ít request hơn / payload nhỏ hơn / load tốt hơn

Sơ đồ vừa khung

Cùng một widget, hai cách lấy dữ liệu. Phần lớn cải thiện đến từ việc hỏi ít hơn, và hỏi thưa hơn.

Biểu đồ và QA cho một dashboard dùng để quản trị

Dashboard dùng để quản trị phải chịu được dữ liệu khó. Màu theo team phải dễ đọc khi số team tăng, team vượt ngưỡng dùng chung một style, và một số thống kê chỉ hiện team có thành viên. Giá trị vượt 100% — tới khoảng 200% — vẫn phải nằm trong biểu đồ, tooltip vẫn đọc được, scale đổi theo rule. Vì vậy QA không dừng ở giao diện: field mapping, công thức total, ngày làm việc, case trùng, sort, status, date range, filter, giá trị 0 và null, và cấu trúc từng API response.

Dự án bao phủ những gì

Bài toán của một hệ thống quản lý lớnThể hiện trong TaxPal
Pipeline sales nhiều trạng tháiStatus của case từ lần liên hệ đầu tới hợp đồng, do saler cập nhật
Quản lý KPITarget theo năm, tháng, nhân viên, loại chỉ tiêu; target so với actual theo ngày, tháng, năm
Tổng hợp nhiều cấpGóc nhìn staff, team, toàn công ty; ranking; doanh số theo dịch vụ trong 4 năm
Rule đảm bảo đúng số liệuNhóm case trùng, ngày làm việc, kỳ quyết toán, nhân viên đã nghỉ
Hiệu năng đọcBảng analytics tính sẵn, chỉ select dữ liệu cần, một API cho nhiều team, lazy load
Phát triển brownfieldModule mới trên codebase AppSync, Prisma và PostgreSQL có sẵn; migration tách pull request riêng
Làm việc với khách hàng NhậtHọp định kỳ, làm rõ yêu cầu, release qua repository của khách hàng

Các sơ đồ được dựng lại ở mức thành phần từ tài liệu dự án. Chúng lược bỏ URL nội bộ, định danh hệ thống, source code độc quyền và dữ liệu của khách hàng. Ảnh chụp là mockup thiết kế và mô hình dữ liệu đã vẽ lại; tên nhân viên, một logo và một ảnh chân dung đã được làm mờ.

Ảnh 1 trên 4