From 126c668915d04b136bbb3f72227adc75fae7f3ac Mon Sep 17 00:00:00 2001 From: zhuyanhuazhuyanhua Date: Wed, 28 May 2025 20:42:27 +0800 Subject: [PATCH 1/3] add BAHTTEXT and DOLLAR with their test files --- calc.go | 90 +++++++++++++++++++++++++++ calc_test.go | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 258 insertions(+) diff --git a/calc.go b/calc.go index c36e500942..b656145fed 100644 --- a/calc.go +++ b/calc.go @@ -18842,3 +18842,93 @@ func (fn *formulaFuncs) DISPIMG(argsList *list.List) formulaArg { } return argsList.Front().Value.(formulaArg) } + +// 定义泰语数字到泰语字符的映射 +var thaiDigits = map[int]string{0: "ศูนย์", 1: "หนึ่ง", 2: "สอง", 3: "สาม", 4: "สี่", 5: "ห้า", 6: "หก", 7: "เจ็ด", 8: "แปด", 9: "เก้า"} + +// 定义泰语数位单位 +var thaiUnits = []string{"", "สิบ", "ร้อย", "พัน", "หมื่น", "แสน", "ล้าน"} + +// numToThaiText 将数字转换为泰语数字文本 +func numToThaiText(num int) string { + if num == 0 { + return thaiDigits[0] + } + var result string + var pos int + for num > 0 { + digit := num % 10 + if digit > 0 { + if pos == 1 && digit == 2 { + result = "ยี่" + thaiUnits[pos] + result + } else if pos == 1 && digit == 1 { + result = thaiUnits[pos] + result + } else if pos == 0 && num > 10 && digit == 1 { + result = "เอ็ด" + result + } else { + result = thaiDigits[digit] + thaiUnits[pos] + result + } + } + num /= 10 + pos++ + } + return result +} + +// func (fn *formulaFuncs) BAHTTEXT 将数字转换为泰铢货币格式的文本 +func (fn *formulaFuncs) BAHTTEXT(argsList *list.List) formulaArg { + // 定义错误信息变量 + const ( + errNoArgMsg = "BAHTTEXT requires at least 1 argument" + errTooManyArgs = "BAHTTEXT requires 1 argument" + ) + + // 验证参数数量 + if argsList.Len() == 0 { + return newErrorFormulaArg(formulaErrorVALUE, errNoArgMsg) + } + if argsList.Len() > 1 { + return newErrorFormulaArg(formulaErrorVALUE, errTooManyArgs) + } + + // 提取并转换参数为数字 + numArg := argsList.Front().Value.(formulaArg) + num := numArg.ToNumber() + + // 检查转换结果是否为数字类型 + if num.Type != ArgNumber { + return num + } + + // 分离整数部分和小数部分 + integerPart := int(num.Number) + decimalPart := int((num.Number - float64(integerPart)) * 100) + + // 定义泰语货币单位变量 + const ( + bahtUnit = "บาท" + satangUnit = "สตางค์" + zeroBahtMsg = "ศูนย์บาทถ้วน" + ) + + // 转换整数部分为泰语文本 + // 转换整数部分为泰语文本 + var integerText string + if integerPart > 0 { + integerText = numToThaiText(integerPart) + bahtUnit + } + + // 转换小数部分为泰语文本 + var decimalText string + if decimalPart > 0 { + decimalText = numToThaiText(decimalPart) + satangUnit + } + + // 组合最终结果 + result := integerText + decimalText + if result == "" { + result = zeroBahtMsg + } + + return newStringFormulaArg(result) +} diff --git a/calc_test.go b/calc_test.go index 4aef37094b..c988a4ea2c 100644 --- a/calc_test.go +++ b/calc_test.go @@ -2,6 +2,7 @@ package excelize import ( "container/list" + "fmt" "math" "path/filepath" "strings" @@ -6502,3 +6503,170 @@ func TestParseToken(t *testing.T) { efp.Token{TSubType: efp.TokenSubTypeRange, TValue: "1A"}, nil, nil, ).Error()) } + +// TestBAHTTEXT 测试 BAHTTEXT 函数 +func TestBAHTTEXT(t *testing.T) { + // 创建一个空的 File 实例和计算上下文 + f := NewFile() + ctx := &calcContext{} + fn := &formulaFuncs{ + f: f, + ctx: ctx, + sheet: "Sheet1", + cell: "A1", + } + + // 定义测试用例结构体 + type testCase struct { + input float64 + expected string + name string + } + + // 定义测试用例 + testCases := []testCase{ + { + input: 0, + expected: "ศูนย์บาทถ้วน", + name: "Zero value", + }, + { + input: 123, + expected: "หนึ่งร้อยยี่สิบสามบาท", + name: "Integer value", + }, + { + input: 123.45, + expected: "หนึ่งร้อยยี่สิบสามบาทสี่สิบห้าสตางค์", + name: "Value with decimal", + }, + { + input: 1000000, + expected: "หนึ่งล้านบาท", + name: "Large integer value", + }, + { + input: 0.50, + expected: "ห้าสิบสตางค์", + name: "Only decimal value", + }, + } + + // 执行测试用例 + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + argsList := list.New() + argsList.PushFront(newNumberFormulaArg(tc.input)) + result := fn.BAHTTEXT(argsList) + + if result.Type != ArgString || result.String != tc.expected { + t.Errorf("Expected %q, but got %q", tc.expected, result.String) + } + }) + } +} + +// TestDOLLAR 测试 DOLLAR 函数 +func TestDOLLAR(t *testing.T) { + // 测试不同区域设置 + cultureInfos := []CultureName{ + CultureNameUnknown, + CultureNameEnUS, + CultureNameJaJP, + CultureNameKoKR, + CultureNameZhCN, + CultureNameZhTW, + } + + // 定义测试用例结构体 + type testCase struct { + inputArgs []float64 + culture CultureName + expected string + name string + expectErr bool + } + + // 定义测试用例 + testCases := []testCase{ + { + inputArgs: []float64{1234.5678}, + culture: CultureNameEnUS, + expected: "$1,234.57", + name: "Single argument, default decimals", + expectErr: false, + }, + { + inputArgs: []float64{1234.5678, 3}, + culture: CultureNameJaJP, + expected: "¥1,234.568", + name: "Two arguments, specified decimals", + expectErr: false, + }, + { + inputArgs: []float64{1234.5678, -1}, + culture: CultureNameZhCN, + expected: "¥1,230", + name: "Negative decimal places", + expectErr: false, + }, + { + inputArgs: []float64{1234.5678, 128}, + culture: CultureNameKoKR, + expected: "#VALUE!", + name: "Decimal value >= 128", + expectErr: true, + }, + { + inputArgs: []float64{}, + culture: CultureNameEnUS, + expected: "#VALUE!", + name: "No arguments", + expectErr: true, + }, + { + inputArgs: []float64{1234.5678, 1, 2}, + culture: CultureNameZhTW, + expected: "#VALUE!", + name: "More than 2 arguments", + expectErr: true, + }, + } + + for _, culture := range cultureInfos { + // 为每个区域设置创建新的 File 实例和计算上下文 + f := NewFile() + f.options.CultureInfo = culture + ctx := &calcContext{} + fn := &formulaFuncs{ + f: f, + ctx: ctx, + sheet: "Sheet1", + cell: "A1", + } + + for _, tc := range testCases { + if tc.culture != culture { + continue + } + t.Run(fmt.Sprintf("%d_%s", tc.culture, tc.name), func(t *testing.T) { + argsList := list.New() + for _, arg := range tc.inputArgs { + argsList.PushBack(newNumberFormulaArg(arg)) + } + + result := fn.DOLLAR(argsList) + + if tc.expectErr { + if result.Type != ArgError { + t.Errorf("Expected error, but got %v", result) + } + } else { + if result.Type != ArgString || result.String != tc.expected { + t.Errorf("Expected %q, but got %q", tc.expected, result.String) + } + } + }) + } + } +} From 99d0e2d07403a79985d6da63e0a82cd7ee46ccbc Mon Sep 17 00:00:00 2001 From: xuri Date: Thu, 4 Sep 2025 10:24:37 +0800 Subject: [PATCH 2/3] Revert changes and update unit tests for BAHTTEXT formula function --- calc.go | 90 ------------------------ calc_test.go | 191 +++++++-------------------------------------------- 2 files changed, 23 insertions(+), 258 deletions(-) diff --git a/calc.go b/calc.go index 695336c17f..6efc715a5d 100644 --- a/calc.go +++ b/calc.go @@ -18985,93 +18985,3 @@ func (fn *formulaFuncs) DISPIMG(argsList *list.List) formulaArg { } return argsList.Front().Value.(formulaArg) } - -// 定义泰语数字到泰语字符的映射 -var thaiDigits = map[int]string{0: "ศูนย์", 1: "หนึ่ง", 2: "สอง", 3: "สาม", 4: "สี่", 5: "ห้า", 6: "หก", 7: "เจ็ด", 8: "แปด", 9: "เก้า"} - -// 定义泰语数位单位 -var thaiUnits = []string{"", "สิบ", "ร้อย", "พัน", "หมื่น", "แสน", "ล้าน"} - -// numToThaiText 将数字转换为泰语数字文本 -func numToThaiText(num int) string { - if num == 0 { - return thaiDigits[0] - } - var result string - var pos int - for num > 0 { - digit := num % 10 - if digit > 0 { - if pos == 1 && digit == 2 { - result = "ยี่" + thaiUnits[pos] + result - } else if pos == 1 && digit == 1 { - result = thaiUnits[pos] + result - } else if pos == 0 && num > 10 && digit == 1 { - result = "เอ็ด" + result - } else { - result = thaiDigits[digit] + thaiUnits[pos] + result - } - } - num /= 10 - pos++ - } - return result -} - -// func (fn *formulaFuncs) BAHTTEXT 将数字转换为泰铢货币格式的文本 -func (fn *formulaFuncs) BAHTTEXT(argsList *list.List) formulaArg { - // 定义错误信息变量 - const ( - errNoArgMsg = "BAHTTEXT requires at least 1 argument" - errTooManyArgs = "BAHTTEXT requires 1 argument" - ) - - // 验证参数数量 - if argsList.Len() == 0 { - return newErrorFormulaArg(formulaErrorVALUE, errNoArgMsg) - } - if argsList.Len() > 1 { - return newErrorFormulaArg(formulaErrorVALUE, errTooManyArgs) - } - - // 提取并转换参数为数字 - numArg := argsList.Front().Value.(formulaArg) - num := numArg.ToNumber() - - // 检查转换结果是否为数字类型 - if num.Type != ArgNumber { - return num - } - - // 分离整数部分和小数部分 - integerPart := int(num.Number) - decimalPart := int((num.Number - float64(integerPart)) * 100) - - // 定义泰语货币单位变量 - const ( - bahtUnit = "บาท" - satangUnit = "สตางค์" - zeroBahtMsg = "ศูนย์บาทถ้วน" - ) - - // 转换整数部分为泰语文本 - // 转换整数部分为泰语文本 - var integerText string - if integerPart > 0 { - integerText = numToThaiText(integerPart) + bahtUnit - } - - // 转换小数部分为泰语文本 - var decimalText string - if decimalPart > 0 { - decimalText = numToThaiText(decimalPart) + satangUnit - } - - // 组合最终结果 - result := integerText + decimalText - if result == "" { - result = zeroBahtMsg - } - - return newStringFormulaArg(result) -} diff --git a/calc_test.go b/calc_test.go index 406eca98d7..2df68aa208 100644 --- a/calc_test.go +++ b/calc_test.go @@ -2,7 +2,6 @@ package excelize import ( "container/list" - "fmt" "math" "path/filepath" "strings" @@ -1708,6 +1707,29 @@ func TestCalcCellValue(t *testing.T) { "ARRAYTOTEXT(A1:D2)": "1, 4, , Month, 2, 5, , Jan", "ARRAYTOTEXT(A1:D2,0)": "1, 4, , Month, 2, 5, , Jan", "ARRAYTOTEXT(A1:D2,1)": "{1,4,,\"Month\";2,5,,\"Jan\"}", + // BAHTTEXT + "BAHTTEXT(-1.1)": "\u0e25\u0e1a\u0e2b\u0e19\u0e36\u0e48\u0e07\u0e1a\u0e32\u0e17\u0e2a\u0e34\u0e1a\u0e2a\u0e15\u0e32\u0e07\u0e04\u0e4c", + "BAHTTEXT(0)": "\u0e28\u0e39\u0e19\u0e22\u0e4c\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(1)": "\u0e2b\u0e19\u0e36\u0e48\u0e07\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(1.1)": "\u0e2b\u0e19\u0e36\u0e48\u0e07\u0e1a\u0e32\u0e17\u0e2a\u0e34\u0e1a\u0e2a\u0e15\u0e32\u0e07\u0e04\u0e4c", + "BAHTTEXT(2)": "\u0e2a\u0e2d\u0e07\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(3)": "\u0e2a\u0e32\u0e21\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(4)": "\u0e2a\u0e35\u0e48\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(5)": "\u0e2b\u0e49\u0e32\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(6)": "\u0e2b\u0e01\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(7)": "\u0e40\u0e08\u0e47\u0e14\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(8)": "\u0e41\u0e1b\u0e14\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(9)": "\u0e40\u0e01\u0e49\u0e32\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(10)": "\u0e2a\u0e34\u0e1a\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(11)": "\u0e2a\u0e34\u0e1a\u0e40\u0e2d\u0e47\u0e14\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(15)": "\u0e2a\u0e34\u0e1a\u0e2b\u0e49\u0e32\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(20)": "\u0e22\u0e35\u0e48\u0e2a\u0e34\u0e1a\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(100)": "\u0e2b\u0e19\u0e36\u0e48\u0e07\u0e23\u0e49\u0e2d\u0e22\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(130)": "\u0e2b\u0e19\u0e36\u0e48\u0e07\u0e23\u0e49\u0e2d\u0e22\u0e2a\u0e32\u0e21\u0e2a\u0e34\u0e1a\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(1000)": "\u0e2b\u0e19\u0e36\u0e48\u0e07\u0e1e\u0e31\u0e19\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(10000)": "\u0e2b\u0e19\u0e36\u0e48\u0e07\u0e2b\u0e21\u0e37\u0e48\u0e19\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(100000)": "\u0e2b\u0e19\u0e36\u0e48\u0e07\u0e41\u0e2a\u0e19\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", + "BAHTTEXT(1000000)": "\u0e2b\u0e19\u0e36\u0e48\u0e07\u0e25\u0e49\u0e32\u0e19\u0e1a\u0e32\u0e17\u0e16\u0e49\u0e27\u0e19", // CHAR "CHAR(65)": "A", "CHAR(97)": "a", @@ -6511,170 +6533,3 @@ func TestParseToken(t *testing.T) { efp.Token{TSubType: efp.TokenSubTypeRange, TValue: "1A"}, nil, nil, ).Error()) } - -// TestBAHTTEXT 测试 BAHTTEXT 函数 -func TestBAHTTEXT(t *testing.T) { - // 创建一个空的 File 实例和计算上下文 - f := NewFile() - ctx := &calcContext{} - fn := &formulaFuncs{ - f: f, - ctx: ctx, - sheet: "Sheet1", - cell: "A1", - } - - // 定义测试用例结构体 - type testCase struct { - input float64 - expected string - name string - } - - // 定义测试用例 - testCases := []testCase{ - { - input: 0, - expected: "ศูนย์บาทถ้วน", - name: "Zero value", - }, - { - input: 123, - expected: "หนึ่งร้อยยี่สิบสามบาท", - name: "Integer value", - }, - { - input: 123.45, - expected: "หนึ่งร้อยยี่สิบสามบาทสี่สิบห้าสตางค์", - name: "Value with decimal", - }, - { - input: 1000000, - expected: "หนึ่งล้านบาท", - name: "Large integer value", - }, - { - input: 0.50, - expected: "ห้าสิบสตางค์", - name: "Only decimal value", - }, - } - - // 执行测试用例 - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - argsList := list.New() - argsList.PushFront(newNumberFormulaArg(tc.input)) - result := fn.BAHTTEXT(argsList) - - if result.Type != ArgString || result.String != tc.expected { - t.Errorf("Expected %q, but got %q", tc.expected, result.String) - } - }) - } -} - -// TestDOLLAR 测试 DOLLAR 函数 -func TestDOLLAR(t *testing.T) { - // 测试不同区域设置 - cultureInfos := []CultureName{ - CultureNameUnknown, - CultureNameEnUS, - CultureNameJaJP, - CultureNameKoKR, - CultureNameZhCN, - CultureNameZhTW, - } - - // 定义测试用例结构体 - type testCase struct { - inputArgs []float64 - culture CultureName - expected string - name string - expectErr bool - } - - // 定义测试用例 - testCases := []testCase{ - { - inputArgs: []float64{1234.5678}, - culture: CultureNameEnUS, - expected: "$1,234.57", - name: "Single argument, default decimals", - expectErr: false, - }, - { - inputArgs: []float64{1234.5678, 3}, - culture: CultureNameJaJP, - expected: "¥1,234.568", - name: "Two arguments, specified decimals", - expectErr: false, - }, - { - inputArgs: []float64{1234.5678, -1}, - culture: CultureNameZhCN, - expected: "¥1,230", - name: "Negative decimal places", - expectErr: false, - }, - { - inputArgs: []float64{1234.5678, 128}, - culture: CultureNameKoKR, - expected: "#VALUE!", - name: "Decimal value >= 128", - expectErr: true, - }, - { - inputArgs: []float64{}, - culture: CultureNameEnUS, - expected: "#VALUE!", - name: "No arguments", - expectErr: true, - }, - { - inputArgs: []float64{1234.5678, 1, 2}, - culture: CultureNameZhTW, - expected: "#VALUE!", - name: "More than 2 arguments", - expectErr: true, - }, - } - - for _, culture := range cultureInfos { - // 为每个区域设置创建新的 File 实例和计算上下文 - f := NewFile() - f.options.CultureInfo = culture - ctx := &calcContext{} - fn := &formulaFuncs{ - f: f, - ctx: ctx, - sheet: "Sheet1", - cell: "A1", - } - - for _, tc := range testCases { - if tc.culture != culture { - continue - } - t.Run(fmt.Sprintf("%d_%s", tc.culture, tc.name), func(t *testing.T) { - argsList := list.New() - for _, arg := range tc.inputArgs { - argsList.PushBack(newNumberFormulaArg(arg)) - } - - result := fn.DOLLAR(argsList) - - if tc.expectErr { - if result.Type != ArgError { - t.Errorf("Expected error, but got %v", result) - } - } else { - if result.Type != ArgString || result.String != tc.expected { - t.Errorf("Expected %q, but got %q", tc.expected, result.String) - } - } - }) - } - } -} From 0b0389a1aa6aae65568ef5bca42f71afa81c5fb5 Mon Sep 17 00:00:00 2001 From: xuri Date: Thu, 4 Sep 2025 10:26:57 +0800 Subject: [PATCH 3/3] Lint code --- calc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/calc.go b/calc.go index 6efc715a5d..5f991bafa2 100644 --- a/calc.go +++ b/calc.go @@ -235,7 +235,7 @@ var ( thBaht = "\u0E1A\u0E32\u0E17" thSatang = "\u0E2A\u0E15\u0E32\u0E07\u0E04\u0E4C" thMinus = "\u0E25\u0E1A" - ) +) // calcContext defines the formula execution context. type calcContext struct {