docs format updated
This commit is contained in:
@@ -289,6 +289,8 @@ type operationSnapshot struct {
|
||||
GatewayService string
|
||||
OperationRef string
|
||||
PaymentRef string
|
||||
ClientName string
|
||||
ClientAddress string
|
||||
OperationCode string
|
||||
OperationLabel string
|
||||
OperationState string
|
||||
@@ -306,6 +308,8 @@ func operationSnapshotFromRequest(req *documentsv1.GetOperationDocumentRequest)
|
||||
GatewayService: strings.TrimSpace(req.GetGatewayService()),
|
||||
OperationRef: strings.TrimSpace(req.GetOperationRef()),
|
||||
PaymentRef: strings.TrimSpace(req.GetPaymentRef()),
|
||||
ClientName: strings.TrimSpace(req.GetClientName()),
|
||||
ClientAddress: strings.TrimSpace(req.GetClientAddress()),
|
||||
OperationCode: strings.TrimSpace(req.GetOperationCode()),
|
||||
OperationLabel: strings.TrimSpace(req.GetOperationLabel()),
|
||||
OperationState: strings.TrimSpace(req.GetOperationState()),
|
||||
@@ -328,21 +332,6 @@ func operationSnapshotFromRequest(req *documentsv1.GetOperationDocumentRequest)
|
||||
func buildOperationBlocks(snapshot operationSnapshot) []renderer.Block {
|
||||
documentCopy := content.OperationDocument
|
||||
|
||||
rows := [][]string{
|
||||
{documentCopy.RowOrganization, snapshot.OrganizationRef},
|
||||
{documentCopy.RowGatewayService, snapshot.GatewayService},
|
||||
{documentCopy.RowOperationRef, snapshot.OperationRef},
|
||||
{documentCopy.RowPaymentRef, safeValue(snapshot.PaymentRef)},
|
||||
{documentCopy.RowCode, safeValue(snapshot.OperationCode)},
|
||||
{documentCopy.RowState, safeValue(snapshot.OperationState)},
|
||||
{documentCopy.RowLabel, safeValue(snapshot.OperationLabel)},
|
||||
{documentCopy.RowStartedAtUTC, formatSnapshotTime(snapshot.StartedAt)},
|
||||
{documentCopy.RowCompletedAtUTC, formatSnapshotTime(snapshot.CompletedAt)},
|
||||
}
|
||||
if snapshot.Amount != "" || snapshot.Currency != "" {
|
||||
rows = append(rows, []string{documentCopy.RowAmount, strings.TrimSpace(strings.TrimSpace(snapshot.Amount) + " " + strings.TrimSpace(snapshot.Currency))})
|
||||
}
|
||||
|
||||
blocks := []renderer.Block{
|
||||
{
|
||||
Tag: renderer.TagTitle,
|
||||
@@ -355,30 +344,115 @@ func buildOperationBlocks(snapshot operationSnapshot) []renderer.Block {
|
||||
{
|
||||
Tag: renderer.TagMeta,
|
||||
Lines: []string{
|
||||
documentCopy.MetaDocumentType,
|
||||
fmt.Sprintf("%s: %s", documentCopy.MetaCertificateNumberLabel, certificateNumber(snapshot)),
|
||||
fmt.Sprintf("%s: %s", documentCopy.MetaDateLabel, formatCertificateDate(certificateDate(snapshot))),
|
||||
},
|
||||
},
|
||||
{
|
||||
Tag: renderer.TagSection,
|
||||
Lines: []string{documentCopy.SectionOperation},
|
||||
Lines: []string{documentCopy.SectionParties},
|
||||
},
|
||||
{
|
||||
Tag: renderer.TagKV,
|
||||
Rows: rows,
|
||||
Tag: renderer.TagText,
|
||||
Lines: []string{documentCopy.PartiesIntro},
|
||||
},
|
||||
{
|
||||
Tag: renderer.TagKV,
|
||||
Rows: [][]string{
|
||||
{documentCopy.RowServiceProvider, content.IssuerLegalName},
|
||||
{documentCopy.RowServiceProviderAddress, content.IssuerLegalAddress},
|
||||
{documentCopy.RowServiceProviderEmail, content.IssuerEmail},
|
||||
{documentCopy.RowClient, certificateClientName(snapshot)},
|
||||
{documentCopy.RowClientAddress, certificateClientAddress(snapshot)},
|
||||
{documentCopy.RowClientReference, safeValue(snapshot.PaymentRef)},
|
||||
},
|
||||
},
|
||||
{
|
||||
Tag: renderer.TagSection,
|
||||
Lines: []string{documentCopy.SectionSubject},
|
||||
},
|
||||
{
|
||||
Tag: renderer.TagText,
|
||||
Lines: []string{
|
||||
documentCopy.SubjectIntro,
|
||||
"",
|
||||
"- Payment execution and orchestration services for payment reference " + safeValue(snapshot.PaymentRef) + ".",
|
||||
"- Gateway service: " + safeValue(snapshot.GatewayService) + ".",
|
||||
"- Operation reference: " + safeValue(snapshot.OperationRef) + ".",
|
||||
"- Operation descriptor: " + operationDescriptor(snapshot) + ".",
|
||||
},
|
||||
},
|
||||
{
|
||||
Tag: renderer.TagSection,
|
||||
Lines: []string{documentCopy.SectionServicePeriod},
|
||||
},
|
||||
{
|
||||
Tag: renderer.TagKV,
|
||||
Rows: [][]string{
|
||||
{documentCopy.RowPeriodFrom, formatSnapshotTime(snapshot.StartedAt)},
|
||||
{documentCopy.RowPeriodTo, formatSnapshotTime(snapshot.CompletedAt)},
|
||||
},
|
||||
},
|
||||
{
|
||||
Tag: renderer.TagSection,
|
||||
Lines: []string{documentCopy.SectionTotalAmount},
|
||||
},
|
||||
{
|
||||
Tag: renderer.TagKV,
|
||||
Rows: [][]string{
|
||||
{documentCopy.RowTotalAmount, operationAmount(snapshot)},
|
||||
},
|
||||
},
|
||||
{
|
||||
Tag: renderer.TagSection,
|
||||
Lines: []string{documentCopy.SectionClientConfirmation},
|
||||
},
|
||||
{
|
||||
Tag: renderer.TagText,
|
||||
Lines: []string{
|
||||
documentCopy.ConfirmationLine1,
|
||||
documentCopy.ConfirmationLine2,
|
||||
documentCopy.ConfirmationLine3,
|
||||
"",
|
||||
"This Certificate serves as confirmation of the completion and acceptance of the services.",
|
||||
},
|
||||
},
|
||||
{
|
||||
Tag: renderer.TagSection,
|
||||
Lines: []string{documentCopy.SectionSignatures},
|
||||
},
|
||||
{
|
||||
Tag: renderer.TagSign,
|
||||
Lines: []string{
|
||||
documentCopy.SignatureServiceProviderLine,
|
||||
"",
|
||||
documentCopy.SignatureClientNamePrefix + " " + certificateClientName(snapshot),
|
||||
documentCopy.SignatureClientTitleLine,
|
||||
documentCopy.SignatureClientLine,
|
||||
},
|
||||
},
|
||||
{
|
||||
Tag: renderer.TagSection,
|
||||
Lines: []string{documentCopy.SectionOperationStatus},
|
||||
},
|
||||
{
|
||||
Tag: renderer.TagKV,
|
||||
Rows: [][]string{
|
||||
{documentCopy.RowOperationStatus, safeValue(snapshot.OperationState)},
|
||||
{documentCopy.RowOperationCode, safeValue(snapshot.OperationCode)},
|
||||
{documentCopy.RowOperationLabel, safeValue(snapshot.OperationLabel)},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if snapshot.FailureCode != "" || snapshot.FailureReason != "" {
|
||||
blocks = append(blocks,
|
||||
renderer.Block{Tag: renderer.TagSection, Lines: []string{documentCopy.SectionFailure}},
|
||||
renderer.Block{
|
||||
Tag: renderer.TagKV,
|
||||
Rows: [][]string{
|
||||
{documentCopy.RowFailureCode, safeValue(snapshot.FailureCode)},
|
||||
{documentCopy.RowFailureReason, safeValue(snapshot.FailureReason)},
|
||||
},
|
||||
blocks = append(blocks, renderer.Block{
|
||||
Tag: renderer.TagKV,
|
||||
Rows: [][]string{
|
||||
{documentCopy.RowFailureCode, safeValue(snapshot.FailureCode)},
|
||||
{documentCopy.RowFailureReason, safeValue(snapshot.FailureReason)},
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return blocks
|
||||
@@ -392,6 +466,80 @@ func formatSnapshotTime(value time.Time) string {
|
||||
return value.UTC().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
func certificateNumber(snapshot operationSnapshot) string {
|
||||
if paymentRef := strings.TrimSpace(snapshot.PaymentRef); paymentRef != "" {
|
||||
return paymentRef
|
||||
}
|
||||
|
||||
if operationRef := strings.TrimSpace(snapshot.OperationRef); operationRef != "" {
|
||||
return operationRef
|
||||
}
|
||||
|
||||
return content.OperationDocument.MissingValuePlaceholder
|
||||
}
|
||||
|
||||
func certificateDate(snapshot operationSnapshot) time.Time {
|
||||
if !snapshot.CompletedAt.IsZero() {
|
||||
return snapshot.CompletedAt.UTC()
|
||||
}
|
||||
|
||||
if !snapshot.StartedAt.IsZero() {
|
||||
return snapshot.StartedAt.UTC()
|
||||
}
|
||||
|
||||
return time.Now().UTC()
|
||||
}
|
||||
|
||||
func formatCertificateDate(value time.Time) string {
|
||||
if value.IsZero() {
|
||||
return content.OperationDocument.MissingValuePlaceholder
|
||||
}
|
||||
|
||||
return value.UTC().Format("January 2, 2006")
|
||||
}
|
||||
|
||||
func operationAmount(snapshot operationSnapshot) string {
|
||||
amount := strings.TrimSpace(snapshot.Amount)
|
||||
currency := strings.TrimSpace(snapshot.Currency)
|
||||
if amount == "" && currency == "" {
|
||||
return content.OperationDocument.MissingValuePlaceholder
|
||||
}
|
||||
|
||||
return strings.TrimSpace(amount + " " + currency)
|
||||
}
|
||||
|
||||
func operationDescriptor(snapshot operationSnapshot) string {
|
||||
label := strings.TrimSpace(snapshot.OperationLabel)
|
||||
code := strings.TrimSpace(snapshot.OperationCode)
|
||||
|
||||
switch {
|
||||
case label != "" && code != "":
|
||||
return fmt.Sprintf("%s (%s)", label, code)
|
||||
case label != "":
|
||||
return label
|
||||
case code != "":
|
||||
return code
|
||||
default:
|
||||
return content.OperationDocument.MissingValuePlaceholder
|
||||
}
|
||||
}
|
||||
|
||||
func certificateClientName(snapshot operationSnapshot) string {
|
||||
if name := strings.TrimSpace(snapshot.ClientName); name != "" {
|
||||
return name
|
||||
}
|
||||
|
||||
return "John Doe"
|
||||
}
|
||||
|
||||
func certificateClientAddress(snapshot operationSnapshot) string {
|
||||
if address := strings.TrimSpace(snapshot.ClientAddress); address != "" {
|
||||
return address
|
||||
}
|
||||
|
||||
return content.OperationDocument.MissingValuePlaceholder
|
||||
}
|
||||
|
||||
func safeValue(value string) string {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if trimmed == "" {
|
||||
|
||||
@@ -3,6 +3,7 @@ package documents
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -191,3 +192,92 @@ func TestGetOperationDocument_RequiresOperationRef(t *testing.T) {
|
||||
t.Fatalf("expected InvalidArgument, got=%v err=%v", status.Code(err), err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildOperationBlocks_CertificateIncludesPaymentData(t *testing.T) {
|
||||
snapshot := operationSnapshot{
|
||||
OrganizationRef: "org-1",
|
||||
GatewayService: "chain_gateway",
|
||||
OperationRef: "op-123",
|
||||
PaymentRef: "pay-123",
|
||||
ClientName: "Jane Customer",
|
||||
ClientAddress: "Main Street 1, City",
|
||||
OperationCode: "transfer",
|
||||
OperationLabel: "Outbound transfer",
|
||||
OperationState: "completed",
|
||||
Amount: "100.50",
|
||||
Currency: "USDT",
|
||||
StartedAt: time.Date(2026, 3, 1, 10, 0, 0, 0, time.UTC),
|
||||
CompletedAt: time.Date(2026, 3, 2, 12, 0, 0, 0, time.UTC),
|
||||
}
|
||||
|
||||
blocks := buildOperationBlocks(snapshot)
|
||||
if len(blocks) == 0 {
|
||||
t.Fatalf("expected blocks")
|
||||
}
|
||||
|
||||
if got := blocks[0].Lines[0]; got != content.OperationDocument.Title {
|
||||
t.Fatalf("title mismatch: got=%q want=%q", got, content.OperationDocument.Title)
|
||||
}
|
||||
|
||||
meta := findTaggedBlock(blocks, renderer.TagMeta)
|
||||
if meta == nil {
|
||||
t.Fatalf("expected meta block")
|
||||
}
|
||||
|
||||
metaText := strings.Join(meta.Lines, "\n")
|
||||
if !strings.Contains(metaText, "Certificate No.: pay-123") {
|
||||
t.Fatalf("meta should include certificate number, got=%q", metaText)
|
||||
}
|
||||
if !strings.Contains(metaText, "Date: March 2, 2026") {
|
||||
t.Fatalf("meta should include certificate date, got=%q", metaText)
|
||||
}
|
||||
|
||||
amountFound := false
|
||||
clientFound := false
|
||||
for _, block := range blocks {
|
||||
if block.Tag != renderer.TagKV {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, row := range block.Rows {
|
||||
if len(row) >= 2 && row[0] == content.OperationDocument.RowTotalAmount && row[1] == "100.50 USDT" {
|
||||
amountFound = true
|
||||
}
|
||||
if len(row) >= 2 && row[0] == content.OperationDocument.RowClient && row[1] == "Jane Customer" {
|
||||
clientFound = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if !amountFound {
|
||||
t.Fatalf("expected total amount row with payment amount")
|
||||
}
|
||||
if !clientFound {
|
||||
t.Fatalf("expected client row with customer name")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertificateNumber_FallsBackToOperationRef(t *testing.T) {
|
||||
got := certificateNumber(operationSnapshot{
|
||||
OperationRef: "op-777",
|
||||
})
|
||||
|
||||
if got != "op-777" {
|
||||
t.Fatalf("certificateNumber fallback mismatch: got=%q want=%q", got, "op-777")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertificateClientName_Fallback(t *testing.T) {
|
||||
if got := certificateClientName(operationSnapshot{}); got != "John Doe" {
|
||||
t.Fatalf("certificateClientName fallback mismatch: got=%q want=%q", got, "John Doe")
|
||||
}
|
||||
}
|
||||
|
||||
func findTaggedBlock(blocks []renderer.Block, tag renderer.Tag) *renderer.Block {
|
||||
for i := range blocks {
|
||||
if blocks[i].Tag == tag {
|
||||
return &blocks[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user