docs format updated
This commit is contained in:
@@ -8,7 +8,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.3
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.11
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.11
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.4
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.0
|
||||
github.com/jung-kurt/gofpdf v1.16.2
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/shopspring/decimal v1.4.0
|
||||
@@ -61,7 +61,7 @@ require (
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
golang.org/x/net v0.51.0 // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
|
||||
@@ -30,8 +30,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19 h1:X1Tow7su
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19/go.mod h1:/rARO8psX+4sfjUQXp5LLifjUt8DuATZ31WptNJTyQA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19 h1:JnQeStZvPHFHeyky/7LbMlyQjUa+jIBj36OlWm0pzIk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19/go.mod h1:HGyasyHvYdFQeJhvDHfH7HXkHh57htcJGKDZ+7z+I24=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.4 h1:4ExZyubQ6LQQVuF2Qp9OsfEvsTdAWh5Gfwf6PgIdLdk=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.4/go.mod h1:NF3JcMGOiARAss1ld3WGORCw71+4ExDD2cbbdKS5PpA=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.0 h1:zyKY4OxzUImu+DigelJI9o49QQv8CjREs5E1CywjtIA=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.0/go.mod h1:NF3JcMGOiARAss1ld3WGORCw71+4ExDD2cbbdKS5PpA=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.7 h1:Y2cAXlClHsXkkOvWZFXATr34b0hxxloeQu/pAZz2row=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.7/go.mod h1:idzZ7gmDeqeNrSPkdbtMp9qWMgcBwykA7P7Rzh5DXVU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.12 h1:iSsvB9EtQ09YrsmIc44Heqlx5ByGErqhPK1ZQLppias=
|
||||
@@ -229,8 +229,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
|
||||
@@ -3,7 +3,8 @@ package content
|
||||
// Issuer details are intentionally centralized to avoid document text drift.
|
||||
const (
|
||||
IssuerLegalName = "SMX Operations Limited"
|
||||
IssuerLegalAddress = "Room 607, 12/F., Block C, Hong Kong Industrial Centre, 489-491 Castle Peak Road, Lai Chi Kok, Hong Kong"
|
||||
IssuerLegalAddress = "Room 607, 12/F., Block C, Hong Kong Industrial Centre, 489-491 Castle Peak Road, Lai Chi Kok, HongKong"
|
||||
IssuerEmail = "contact@sendico.io"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -74,43 +75,77 @@ var AcceptanceTemplate = AcceptanceTemplateContent{
|
||||
|
||||
// OperationDocumentContent contains all static copy for operation documents.
|
||||
type OperationDocumentContent struct {
|
||||
Title string
|
||||
Subtitle string
|
||||
MetaDocumentType string
|
||||
SectionOperation string
|
||||
SectionFailure string
|
||||
RowOrganization string
|
||||
RowGatewayService string
|
||||
RowOperationRef string
|
||||
RowPaymentRef string
|
||||
RowCode string
|
||||
RowState string
|
||||
RowLabel string
|
||||
RowStartedAtUTC string
|
||||
RowCompletedAtUTC string
|
||||
RowAmount string
|
||||
RowFailureCode string
|
||||
RowFailureReason string
|
||||
MissingValuePlaceholder string
|
||||
Title string
|
||||
Subtitle string
|
||||
MetaCertificateNumberLabel string
|
||||
MetaDateLabel string
|
||||
SectionParties string
|
||||
PartiesIntro string
|
||||
RowServiceProvider string
|
||||
RowServiceProviderAddress string
|
||||
RowServiceProviderEmail string
|
||||
RowClient string
|
||||
RowClientAddress string
|
||||
RowClientReference string
|
||||
SectionSubject string
|
||||
SubjectIntro string
|
||||
SectionServicePeriod string
|
||||
RowPeriodFrom string
|
||||
RowPeriodTo string
|
||||
SectionTotalAmount string
|
||||
RowTotalAmount string
|
||||
SectionClientConfirmation string
|
||||
ConfirmationLine1 string
|
||||
ConfirmationLine2 string
|
||||
ConfirmationLine3 string
|
||||
SectionSignatures string
|
||||
SignatureServiceProviderLine string
|
||||
SignatureClientNamePrefix string
|
||||
SignatureClientTitleLine string
|
||||
SignatureClientLine string
|
||||
SectionOperationStatus string
|
||||
RowOperationStatus string
|
||||
RowOperationCode string
|
||||
RowOperationLabel string
|
||||
RowFailureCode string
|
||||
RowFailureReason string
|
||||
MissingValuePlaceholder string
|
||||
}
|
||||
|
||||
var OperationDocument = OperationDocumentContent{
|
||||
Title: "OPERATION BILLING DOCUMENT",
|
||||
Subtitle: "Gateway operation statement",
|
||||
MetaDocumentType: "Document Type: Operation",
|
||||
SectionOperation: "OPERATION DETAILS",
|
||||
SectionFailure: "FAILURE DETAILS",
|
||||
RowOrganization: "Organization",
|
||||
RowGatewayService: "Gateway Service",
|
||||
RowOperationRef: "Operation Ref",
|
||||
RowPaymentRef: "Payment Ref",
|
||||
RowCode: "Code",
|
||||
RowState: "State",
|
||||
RowLabel: "Label",
|
||||
RowStartedAtUTC: "Started At (UTC)",
|
||||
RowCompletedAtUTC: "Completed At (UTC)",
|
||||
RowAmount: "Amount",
|
||||
RowFailureCode: "Failure Code",
|
||||
RowFailureReason: "Failure Reason",
|
||||
MissingValuePlaceholder: "n/a",
|
||||
Title: "CERTIFICATE OF SERVICES RENDERED",
|
||||
Subtitle: "Payment operation completion and acceptance statement",
|
||||
MetaCertificateNumberLabel: "Certificate No.",
|
||||
MetaDateLabel: "Date",
|
||||
SectionParties: "PARTIES",
|
||||
PartiesIntro: "This Certificate is made between:",
|
||||
RowServiceProvider: "Service Provider",
|
||||
RowServiceProviderAddress: "Service Provider Address",
|
||||
RowServiceProviderEmail: "Service Provider Email",
|
||||
RowClient: "Client",
|
||||
RowClientAddress: "Client Address",
|
||||
RowClientReference: "Client Reference",
|
||||
SectionSubject: "SUBJECT OF THE CERTIFICATE",
|
||||
SubjectIntro: "The Service Provider confirms that the following services have been fully rendered to the Client:",
|
||||
SectionServicePeriod: "SERVICE PERIOD",
|
||||
RowPeriodFrom: "From",
|
||||
RowPeriodTo: "To",
|
||||
SectionTotalAmount: "TOTAL AMOUNT",
|
||||
RowTotalAmount: "Amount",
|
||||
SectionClientConfirmation: "CLIENT CONFIRMATION",
|
||||
ConfirmationLine1: "- the services were rendered in full;",
|
||||
ConfirmationLine2: "- the services were rendered properly and within the agreed scope;",
|
||||
ConfirmationLine3: "- the Client has no claims regarding the quality, quantity, or timing of the services rendered.",
|
||||
SectionSignatures: "SIGNATURES",
|
||||
SignatureServiceProviderLine: "Service Provider: Name: SMX Operations Limited | Signature: __________________",
|
||||
SignatureClientNamePrefix: "Client: Name:",
|
||||
SignatureClientTitleLine: "Title: Authorized Representative",
|
||||
SignatureClientLine: "Signature: __________________",
|
||||
SectionOperationStatus: "OPERATION STATUS",
|
||||
RowOperationStatus: "Operation Status",
|
||||
RowOperationCode: "Operation Code",
|
||||
RowOperationLabel: "Operation Label",
|
||||
RowFailureCode: "Failure Code",
|
||||
RowFailureReason: "Failure Reason",
|
||||
MissingValuePlaceholder: "n/a",
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ require (
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
golang.org/x/net v0.51.0 // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
|
||||
@@ -179,8 +179,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
|
||||
Reference in New Issue
Block a user