fix(service): populate Excel pricing cells and verify attachment readback parity
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
weichen
co-authored by
Cursor
parent
59cca0594e
commit
c65f5e8a3d
@@ -1,12 +1,17 @@
|
||||
package bizdev_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/xuri/excelize/v2"
|
||||
|
||||
"chat-agent-system/internal/bizdev"
|
||||
"chat-agent-system/internal/config"
|
||||
"chat-agent-system/internal/quote"
|
||||
@@ -78,4 +83,70 @@ func TestExecuteTaskIndependent(t *testing.T) {
|
||||
if !strings.Contains(quoteRes.MarkdownBody, "微创吻合器研发") {
|
||||
t.Errorf("markdown missing product_name")
|
||||
}
|
||||
|
||||
xlsxPrice, err := readQuoteSheetTotal(quoteRes.Attachments[0].Data)
|
||||
if err != nil {
|
||||
t.Fatalf("read Excel J18 from attachment: %v", err)
|
||||
}
|
||||
if diff := xlsxPrice - summary.TotalPrice; diff < -0.01 || diff > 0.01 {
|
||||
t.Errorf("Excel J18 total %v != summary total %v", xlsxPrice, summary.TotalPrice)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteTaskWritesMaterials(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
workDir := filepath.Join(tmpDir, "workspace")
|
||||
cfg := &config.Config{
|
||||
DataDir: tmpDir,
|
||||
AgentBackend: "stub",
|
||||
AgentTimeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
svc := bizdev.NewService(cfg)
|
||||
materialContent := []byte("客户提供的补充需求说明")
|
||||
res, err := svc.ExecuteTask(context.Background(), bizdev.TaskRunRequest{
|
||||
TaskKey: "intake",
|
||||
ProjectName: "测试项目",
|
||||
CustomerName: "测试客户",
|
||||
WorkDir: workDir,
|
||||
Materials: []bizdev.MaterialInput{
|
||||
{Filename: "requirements.txt", Content: materialContent},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("execute intake with materials failed: %v", err)
|
||||
}
|
||||
|
||||
materialPath := filepath.Join(res.WorkDir, "materials_text", "requirements.txt")
|
||||
data, err := os.ReadFile(materialPath)
|
||||
if err != nil {
|
||||
t.Fatalf("expected material file at %s: %v", materialPath, err)
|
||||
}
|
||||
if !bytes.Equal(data, materialContent) {
|
||||
t.Errorf("material content mismatch: got %q, want %q", data, materialContent)
|
||||
}
|
||||
}
|
||||
|
||||
func readQuoteSheetTotal(xlsxData []byte) (float64, error) {
|
||||
f, err := excelize.OpenReader(bytes.NewReader(xlsxData))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
const sheet = "报价模型"
|
||||
raw, err := f.GetCellValue(sheet, "J18")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return parseExcelNumber(raw)
|
||||
}
|
||||
|
||||
func parseExcelNumber(raw string) (float64, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return 0, nil
|
||||
}
|
||||
raw = strings.ReplaceAll(raw, ",", "")
|
||||
return strconv.ParseFloat(raw, 64)
|
||||
}
|
||||
|
||||
@@ -78,6 +78,28 @@ func CalculateAndFill(in Input, templateXlsx string, outPath string) (*Summary,
|
||||
if idx, _ := f.GetSheetIndex(sheet); idx != -1 {
|
||||
_ = f.SetCellValue(sheet, "B3", in.CustomerName)
|
||||
_ = f.SetCellValue(sheet, "B4", in.ProductName)
|
||||
_ = f.SetCellValue(sheet, "B5", totalPrice)
|
||||
|
||||
writeSummaryLine(f, sheet, 8, summary.Lines[0])
|
||||
writeSummaryLine(f, sheet, 9, summary.Lines[1])
|
||||
writeSummaryLine(f, sheet, 10, summary.Lines[2])
|
||||
|
||||
var totalPersonYear, totalCost, totalBuffer, totalMargin, totalTax float64
|
||||
for _, line := range summary.Lines {
|
||||
totalPersonYear += line.PersonYear
|
||||
totalCost += line.Cost
|
||||
totalBuffer += line.Buffer
|
||||
totalMargin += line.Margin
|
||||
totalTax += line.Tax
|
||||
}
|
||||
writeSummaryLine(f, sheet, 18, SummaryLine{
|
||||
PersonYear: totalPersonYear,
|
||||
Cost: totalCost,
|
||||
Buffer: totalBuffer,
|
||||
Margin: totalMargin,
|
||||
Tax: totalTax,
|
||||
Price: totalPrice,
|
||||
})
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(outPath), 0o755); err != nil {
|
||||
@@ -89,3 +111,12 @@ func CalculateAndFill(in Input, templateXlsx string, outPath string) (*Summary,
|
||||
|
||||
return summary, nil
|
||||
}
|
||||
|
||||
func writeSummaryLine(f *excelize.File, sheet string, row int, line SummaryLine) {
|
||||
_ = f.SetCellValue(sheet, fmt.Sprintf("D%d", row), line.PersonYear)
|
||||
_ = f.SetCellValue(sheet, fmt.Sprintf("E%d", row), line.Cost)
|
||||
_ = f.SetCellValue(sheet, fmt.Sprintf("F%d", row), line.Buffer)
|
||||
_ = f.SetCellValue(sheet, fmt.Sprintf("G%d", row), line.Margin)
|
||||
_ = f.SetCellValue(sheet, fmt.Sprintf("H%d", row), line.Tax)
|
||||
_ = f.SetCellValue(sheet, fmt.Sprintf("J%d", row), line.Price)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user