mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 14:53:44 +08:00
1 parent
2904a0d914
commit
3709a62c24
3 files changed
+294
-6
No files matched your search
@@ -1088,6 +1088,11 @@ func invoiceBindingCoverage(before, after Invoice, bindings []InvoiceBinding) er
|
|||||||
if a.Name != b.Name || a.Namespace != b.Namespace || a.Context != b.Context || a.Unit != b.Unit {
|
if a.Name != b.Name || a.Namespace != b.Namespace || a.Context != b.Context || a.Unit != b.Unit {
|
||||||
return fmt.Errorf("bound invoice fact identity changed")
|
return fmt.Errorf("bound invoice fact identity changed")
|
||||||
}
|
}
|
||||||
|
if mapped, ok := invoiceEDRMFactBinding(before, a); ok {
|
||||||
|
if next, valid := invoiceEDRMFactBinding(after, b); valid && mapped.Field == next.Field && mapped.Item == next.Item {
|
||||||
|
bound[fieldKey{mapped.Field, mapped.Item}] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for key, value := range old {
|
for key, value := range old {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InvoiceLayout 指定发票信息页的字体资源及毫米尺寸,不代表税务机关的固定票样
|
// InvoiceLayout 指定发票信息页的字体资源及毫米尺寸,不代表税务机关的固定票样
|
||||||
@@ -123,6 +124,18 @@ func invoiceLayoutFactVisible(invoice Invoice, fact InvoiceFact) bool {
|
|||||||
if fact.Nil || fact.Value == "" || fact.Namespace == invoiceXBRL && (fact.Name == "context" || fact.Name == "unit") {
|
if fact.Nil || fact.Value == "" || fact.Namespace == invoiceXBRL && (fact.Name == "context" || fact.Name == "unit") {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if binding, ok := invoiceEDRMFactBinding(invoice, fact); ok {
|
||||||
|
value, err := invoiceBindingValue(invoice, binding)
|
||||||
|
raw := fact.Value
|
||||||
|
if binding.Field == "date" {
|
||||||
|
if date, err := time.Parse("2006年01月02日", raw); err == nil {
|
||||||
|
raw = date.Format("2006-01-02")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err == nil && value == raw {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
kind, _ := invoiceTaxonomy(fact.Namespace)
|
kind, _ := invoiceTaxonomy(fact.Namespace)
|
||||||
if kind != "" && kind == invoice.Kind {
|
if kind != "" && kind == invoice.Kind {
|
||||||
if field := invoiceXMLField(&invoice, kind, fact.Name); field != nil && *field == fact.Value {
|
if field := invoiceXMLField(&invoice, kind, fact.Name); field != nil && *field == fact.Value {
|
||||||
@@ -163,6 +176,46 @@ func invoiceLayoutFactVisible(invoice Invoice, fact InvoiceFact) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// invoiceEDRMFactBinding 按2019版完整路径识别摘要与原始字段的同一语义
|
||||||
|
// 入参: value 发票,fact 原始字段
|
||||||
|
// 返回: 公共字段关联及是否明确映射
|
||||||
|
func invoiceEDRMFactBinding(value Invoice, fact InvoiceFact) (InvoiceBinding, bool) {
|
||||||
|
if value.Standard != invoiceEDRM || fact.Namespace != invoiceEDRM {
|
||||||
|
return InvoiceBinding{}, false
|
||||||
|
}
|
||||||
|
var path strings.Builder
|
||||||
|
for _, segment := range strings.Split(strings.TrimPrefix(strings.ReplaceAll(fact.Source.NodePath, "{"+invoiceEDRM+"}", "{}"), "/"), "/") {
|
||||||
|
name, index, ok := strings.Cut(segment, "[")
|
||||||
|
if !ok || !strings.HasPrefix(name, "{}") || !strings.HasSuffix(index, "]") {
|
||||||
|
return InvoiceBinding{}, false
|
||||||
|
}
|
||||||
|
path.WriteByte('/')
|
||||||
|
path.WriteString(strings.TrimPrefix(name, "{}"))
|
||||||
|
}
|
||||||
|
probe := Invoice{Items: []InvoiceItem{{}}}
|
||||||
|
field, _ := invoiceEDRMField(&probe, &probe.Items[0], path.String())
|
||||||
|
if field == nil {
|
||||||
|
return InvoiceBinding{}, false
|
||||||
|
}
|
||||||
|
*field = "mapped"
|
||||||
|
for _, candidate := range invoiceLayoutFields(probe) {
|
||||||
|
if candidate.value != "mapped" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if candidate.item < 0 {
|
||||||
|
return InvoiceBinding{Field: candidate.field, Item: -1}, true
|
||||||
|
}
|
||||||
|
for i, item := range value.Items {
|
||||||
|
for _, existing := range item.Fields {
|
||||||
|
if existing.Source.NodePath == fact.Source.NodePath {
|
||||||
|
return InvoiceBinding{Field: candidate.field, Item: i}, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return InvoiceBinding{}, false
|
||||||
|
}
|
||||||
|
|
||||||
// invoiceBindingValue 读取明确字段与明细索引,不接受未知字段或隐式索引
|
// invoiceBindingValue 读取明确字段与明细索引,不接受未知字段或隐式索引
|
||||||
// 入参: invoice 发票数据, binding 字段绑定
|
// 入参: invoice 发票数据, binding 字段绑定
|
||||||
// 返回: string 字段原文, error 字段或索引错误
|
// 返回: string 字段原文, error 字段或索引错误
|
||||||
|
|||||||
+236
-6
@@ -27,6 +27,7 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -117,7 +118,13 @@ type invoiceXMLPatch struct {
|
|||||||
func InvoiceFields(value Invoice) []InvoiceFieldDescriptor {
|
func InvoiceFields(value Invoice) []InvoiceFieldDescriptor {
|
||||||
format, supported := invoiceWriteFormat(value)
|
format, supported := invoiceWriteFormat(value)
|
||||||
probe := Invoice{Items: []InvoiceItem{{}}}
|
probe := Invoice{Items: []InvoiceItem{{}}}
|
||||||
if value.Standard == InvoiceStandardEInvoice {
|
if value.Standard == invoiceEDRM {
|
||||||
|
for _, path := range invoiceEDRMPaths() {
|
||||||
|
if p, _ := invoiceEDRMField(&probe, &probe.Items[0], path); p != nil {
|
||||||
|
*p = "editable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if value.Standard == InvoiceStandardEInvoice {
|
||||||
probe.Number, probe.Date = "editable", "editable"
|
probe.Number, probe.Date = "editable", "editable"
|
||||||
for _, group := range invoicePlainGroups() {
|
for _, group := range invoicePlainGroups() {
|
||||||
for _, name := range strings.Fields(group.value) {
|
for _, name := range strings.Fields(group.value) {
|
||||||
@@ -209,7 +216,7 @@ func InvoiceFields(value Invoice) []InvoiceFieldDescriptor {
|
|||||||
// 返回: 已适配格式列表
|
// 返回: 已适配格式列表
|
||||||
func InvoiceFormats() []InvoiceFormat {
|
func InvoiceFormats() []InvoiceFormat {
|
||||||
result := []InvoiceFormat{{Kind: "einv", Standard: InvoiceStandardEInvoice, Version: "0.3", Read: true, Create: true, Edit: true, Items: true}}
|
result := []InvoiceFormat{{Kind: "einv", Standard: InvoiceStandardEInvoice, Version: "0.3", Read: true, Create: true, Edit: true, Items: true}}
|
||||||
result = append(result, InvoiceFormat{Kind: "inv", Standard: invoiceEDRM, Version: "1.0", Read: true, Items: true})
|
result = append(result, InvoiceFormat{Kind: "inv", Standard: invoiceEDRM, Version: "1.0", Read: true, Edit: true, Items: true})
|
||||||
result = append(result, InvoiceFormat{Kind: "inv", Standard: ofdNamespace2016, Version: "1.0", Read: true})
|
result = append(result, InvoiceFormat{Kind: "inv", Standard: ofdNamespace2016, Version: "1.0", Read: true})
|
||||||
for _, standard := range []string{InvoiceStandardVAT2023, InvoiceStandardDigital2023, InvoiceStandardAir2023, InvoiceStandardRail2023} {
|
for _, standard := range []string{InvoiceStandardVAT2023, InvoiceStandardDigital2023, InvoiceStandardAir2023, InvoiceStandardRail2023} {
|
||||||
kind, version := invoiceTaxonomy(standard)
|
kind, version := invoiceTaxonomy(standard)
|
||||||
@@ -1058,14 +1065,20 @@ func invoiceWriteXMLProblems(ctx context.Context, data []byte, tuples bool) (map
|
|||||||
empty := nilValue == "true" || nilValue == "1"
|
empty := nilValue == "true" || nilValue == "1"
|
||||||
date := kind != "" && invoiceWriteDateFact(kind, n.name.Local)
|
date := kind != "" && invoiceWriteDateFact(kind, n.name.Local)
|
||||||
plainDate := root.name == (xml.Name{Local: "EInvoice"}) && path == "/EInvoice/TaxSupervisionInfo/IssueTime"
|
plainDate := root.name == (xml.Name{Local: "EInvoice"}) && path == "/EInvoice/TaxSupervisionInfo/IssueTime"
|
||||||
if len(n.children) == 0 && !empty && (date || plainDate) {
|
edrmDate := n.parent == root && n.name == (xml.Name{Space: invoiceEDRM, Local: "IssueDate"})
|
||||||
|
if len(n.children) == 0 && !empty && (date || plainDate || edrmDate) {
|
||||||
value := n.text.String()
|
value := n.text.String()
|
||||||
parseValue := value
|
parseValue := value
|
||||||
if plainDate {
|
if plainDate {
|
||||||
parseValue = strings.Replace(value, " ", "T", 1)
|
parseValue = strings.Replace(value, " ", "T", 1)
|
||||||
}
|
}
|
||||||
|
if edrmDate {
|
||||||
|
if d, err := time.Parse("2006年01月02日", value); err == nil {
|
||||||
|
parseValue = d.Format("2006-01-02")
|
||||||
|
}
|
||||||
|
}
|
||||||
_, valid := invoicePeriodTime(parseValue)
|
_, valid := invoicePeriodTime(parseValue)
|
||||||
if !valid || date && strings.ContainsAny(value, "T ") {
|
if !valid || date && strings.ContainsAny(value, "T ") || edrmDate && len(parseValue) != 10 {
|
||||||
add(InvoiceIssue{Code: "invalid-date", Field: n.name.Local, Message: "invalid invoice date", Source: InvoiceSource{NodePath: n.path}}, value)
|
add(InvoiceIssue{Code: "invalid-date", Field: n.name.Local, Message: "invalid invoice date", Source: InvoiceSource{NodePath: n.path}}, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1092,6 +1105,18 @@ func invoiceWriteXMLProblems(ctx context.Context, data []byte, tuples bool) (map
|
|||||||
if err := walk(root, ""); err != nil {
|
if err := walk(root, ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
for _, value := range report.Invoices {
|
||||||
|
if value.Standard != invoiceEDRM {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
issues, err := ValidateInvoice(ctx, value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, issue := range issues {
|
||||||
|
add(issue, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, issue := range report.Issues {
|
for _, issue := range report.Issues {
|
||||||
value := ""
|
value := ""
|
||||||
if n := nodes[issue.Source.NodePath]; n != nil {
|
if n := nodes[issue.Source.NodePath]; n != nil {
|
||||||
@@ -1212,6 +1237,9 @@ func UpdateInvoiceXML(ctx context.Context, data []byte, invoiceIndex int, value
|
|||||||
if err = invoiceWriteNewIssues(filter(oldIssues), filter(newIssues)); err != nil {
|
if err = invoiceWriteNewIssues(filter(oldIssues), filter(newIssues)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if value.Standard == invoiceEDRM {
|
||||||
|
return invoiceUpdateEDRM(ctx, data, old, value)
|
||||||
|
}
|
||||||
root, spans, err := invoiceWriteSpans(ctx, data)
|
root, spans, err := invoiceWriteSpans(ctx, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -1453,6 +1481,180 @@ func UpdateInvoiceXML(ctx context.Context, data []byte, invoiceIndex int, value
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// invoiceEDRMPaths 列出2019版摘要与明细的映射路径
|
||||||
|
// 入参: 无
|
||||||
|
// 返回: 2019版可映射字段路径
|
||||||
|
func invoiceEDRMPaths() []string {
|
||||||
|
return strings.Fields("/eInvoice/InvoiceCode /eInvoice/InvoiceNo /eInvoice/IssueDate /eInvoice/Buyer/BuyerName /eInvoice/Buyer/BuyerTaxID /eInvoice/Seller/SellerName /eInvoice/Seller/SellerTaxID /eInvoice/TaxInclusiveTotalAmount /eInvoice/Note /eInvoice/TaxTotalAmount /eInvoice/TaxExclusiveTotalAmount /eInvoice/GoodsInfos/GoodsInfo/Item /eInvoice/GoodsInfos/GoodsInfo/Specification /eInvoice/GoodsInfos/GoodsInfo/MeasurementDimension /eInvoice/GoodsInfos/GoodsInfo/Quantity /eInvoice/GoodsInfos/GoodsInfo/Price /eInvoice/GoodsInfos/GoodsInfo/Amount /eInvoice/GoodsInfos/GoodsInfo/TaxScheme /eInvoice/GoodsInfos/GoodsInfo/TaxAmount")
|
||||||
|
}
|
||||||
|
|
||||||
|
// invoiceUpdateEDRM 按2019版原节点写回摘要及明细,保留扩展与未修改字节
|
||||||
|
// 入参: ctx 上下文,data 原文,old 原发票,value 目标发票
|
||||||
|
// 返回: 修改后XML及错误
|
||||||
|
func invoiceUpdateEDRM(ctx context.Context, data []byte, old, value Invoice) ([]byte, error) {
|
||||||
|
if err := invoiceWriteUnmapped(old.Facts, value.Facts); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
root, spans, err := invoiceWriteSpans(ctx, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, name := range []string{"Buyer", "Seller", "GoodsInfos"} {
|
||||||
|
changed := name == "GoodsInfos" && !reflect.DeepEqual(old.Items, value.Items) || name == "Buyer" && old.Buyer != value.Buyer || name == "Seller" && old.Seller != value.Seller
|
||||||
|
groups := invoiceChildren(root, invoiceEDRM, name)
|
||||||
|
if len(groups) > 1 {
|
||||||
|
return nil, fmt.Errorf("ambiguous invoice group %s", name)
|
||||||
|
}
|
||||||
|
if changed && len(groups) == 1 && spans[groups[0].path].empty {
|
||||||
|
patch, err := invoiceWriteInsert(spans[groups[0].path], nil, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
normalized, err := invoiceApplyXMLPatches(ctx, data, []invoiceXMLPatch{patch})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return invoiceUpdateEDRM(ctx, normalized, old, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var patches []invoiceXMLPatch
|
||||||
|
order := strings.Fields("DocID InvoiceCode InvoiceNo TypeCode IssueDate InvoiceCheckCode MachineNo TaxControlCode Buyer Tpvn Seller TaxInclusiveTotalAmount Note InvoiceClerk Payee Checker TaxTotalAmount TaxExclusiveTotalAmount GraphCode InvoiceSIA1 InvoiceSIA2 Signature GoodsInfos")
|
||||||
|
for _, group := range []string{"", "Buyer", "Seller"} {
|
||||||
|
parent := root
|
||||||
|
if group != "" {
|
||||||
|
parents := invoiceChildren(root, invoiceEDRM, group)
|
||||||
|
if len(parents) > 0 {
|
||||||
|
parent = parents[0]
|
||||||
|
} else {
|
||||||
|
parent = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
desired := &invoiceXMLNode{name: xml.Name{Space: invoiceEDRM, Local: group}}
|
||||||
|
var names []string
|
||||||
|
for _, path := range invoiceEDRMPaths() {
|
||||||
|
parts := strings.Split(path, "/")
|
||||||
|
if group == "" && len(parts) != 3 || group != "" && (len(parts) != 4 || parts[2] != group) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
previous, _ := invoiceEDRMField(&old, nil, path)
|
||||||
|
target, _ := invoiceEDRMField(&value, nil, path)
|
||||||
|
if *previous == *target {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := parts[len(parts)-1]
|
||||||
|
names = append(names, name)
|
||||||
|
node := &invoiceXMLNode{name: xml.Name{Space: invoiceEDRM, Local: name}}
|
||||||
|
text := *target
|
||||||
|
if name == "IssueDate" && parent != nil {
|
||||||
|
existing := invoiceChildren(parent, invoiceEDRM, name)
|
||||||
|
if len(existing) == 1 && strings.Contains(existing[0].text.String(), "年") && text != "" {
|
||||||
|
date, err := time.Parse("2006-01-02", text)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
text = date.Format("2006年01月02日")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.text.WriteString(text)
|
||||||
|
node.parts = []invoiceXMLPart{{text: text}}
|
||||||
|
desired.children = append(desired.children, node)
|
||||||
|
desired.parts = append(desired.parts, invoiceXMLPart{child: node})
|
||||||
|
}
|
||||||
|
if len(names) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if parent == nil {
|
||||||
|
raw, err := invoiceWriteNode(desired)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
patch, err := invoiceWriteOrderedInsert(data, spans, root, invoiceEDRM, group, order, raw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
patches = append(patches, patch)
|
||||||
|
} else {
|
||||||
|
for _, name := range names {
|
||||||
|
if len(invoiceChildren(parent, invoiceEDRM, name)) == 0 && group == "" {
|
||||||
|
raw, err := invoiceWriteNode(invoiceChildren(desired, invoiceEDRM, name)[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
patch, err := invoiceWriteOrderedInsert(data, spans, parent, invoiceEDRM, name, order, raw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
patches = append(patches, patch)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
changes, err := invoiceWriteMergeChildren(data, spans, parent, desired, invoiceEDRM, []string{name})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
patches = append(patches, changes...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(old.Items, value.Items) {
|
||||||
|
generated := &invoiceXMLNode{name: xml.Name{Space: invoiceEDRM, Local: "GoodsInfos"}}
|
||||||
|
for _, item := range value.Items {
|
||||||
|
node := &invoiceXMLNode{name: xml.Name{Space: invoiceEDRM, Local: "GoodsInfo"}}
|
||||||
|
for _, path := range invoiceEDRMPaths() {
|
||||||
|
if !strings.HasPrefix(path, "/eInvoice/GoodsInfos/GoodsInfo/") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p, _ := invoiceEDRMField(&value, &item, path)
|
||||||
|
leaf := &invoiceXMLNode{name: xml.Name{Space: invoiceEDRM, Local: path[strings.LastIndexByte(path, '/')+1:]}}
|
||||||
|
leaf.text.WriteString(*p)
|
||||||
|
leaf.parts = []invoiceXMLPart{{text: *p}}
|
||||||
|
node.children = append(node.children, leaf)
|
||||||
|
node.parts = append(node.parts, invoiceXMLPart{child: leaf})
|
||||||
|
}
|
||||||
|
generated.children = append(generated.children, node)
|
||||||
|
generated.parts = append(generated.parts, invoiceXMLPart{child: node})
|
||||||
|
}
|
||||||
|
parents := invoiceChildren(root, invoiceEDRM, "GoodsInfos")
|
||||||
|
if len(parents) == 0 {
|
||||||
|
raw, err := invoiceWriteNode(generated)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
patch, err := invoiceWriteInsert(spans[root.path], raw, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
patches = append(patches, patch)
|
||||||
|
} else {
|
||||||
|
changes, err := invoiceWriteMergeItems(ctx, data, spans, parents[0], generated, old, value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
patches = append(patches, changes...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result, err := invoiceApplyXMLPatches(ctx, data, patches)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
report, err := DecodeInvoices(ctx, result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(report.Invoices) != 1 {
|
||||||
|
return nil, fmt.Errorf("invoice update changed candidate count")
|
||||||
|
}
|
||||||
|
if err := invoiceWriteRepresented(value, report.Invoices[0]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := invoiceWriteRepresented(report.Invoices[0], value); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := invoiceValidateXMLWrite(ctx, data, result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// invoiceWriteContains 判断字段是否位于已知顺序表
|
// invoiceWriteContains 判断字段是否位于已知顺序表
|
||||||
// 入参: names 字段列表,name 字段
|
// 入参: names 字段列表,name 字段
|
||||||
// 返回: 是否存在
|
// 返回: 是否存在
|
||||||
@@ -1685,7 +1887,10 @@ func invoiceWriteMergeItems(ctx context.Context, data []byte, spans map[string]i
|
|||||||
}
|
}
|
||||||
space, name := value.Standard, invoiceWriteTuple(value.Kind)
|
space, name := value.Standard, invoiceWriteTuple(value.Kind)
|
||||||
names := invoiceWriteItemNames(value.Kind)
|
names := invoiceWriteItemNames(value.Kind)
|
||||||
if value.Standard == InvoiceStandardEInvoice {
|
if value.Standard == invoiceEDRM {
|
||||||
|
name = "GoodsInfo"
|
||||||
|
names = strings.Fields("Item Specification MeasurementDimension Quantity Price Amount TaxScheme TaxAmount")
|
||||||
|
} else if value.Standard == InvoiceStandardEInvoice {
|
||||||
space = ""
|
space = ""
|
||||||
name = "IssuItemInformation"
|
name = "IssuItemInformation"
|
||||||
names = strings.Fields("ItemName SpecMod MeaUnits Quantity UnPrice Amount TaxRate ComTaxAm")
|
names = strings.Fields("ItemName SpecMod MeaUnits Quantity UnPrice Amount TaxRate ComTaxAm")
|
||||||
@@ -1695,6 +1900,7 @@ func invoiceWriteMergeItems(ctx context.Context, data []byte, spans map[string]i
|
|||||||
return nil, fmt.Errorf("unsupported invoice item shape")
|
return nil, fmt.Errorf("unsupported invoice item shape")
|
||||||
}
|
}
|
||||||
original := map[string]*invoiceXMLNode{}
|
original := map[string]*invoiceXMLNode{}
|
||||||
|
originalItems := map[string]InvoiceItem{}
|
||||||
for _, item := range old.Items {
|
for _, item := range old.Items {
|
||||||
for _, f := range item.Fields {
|
for _, f := range item.Fields {
|
||||||
s := spans[f.Source.NodePath]
|
s := spans[f.Source.NodePath]
|
||||||
@@ -1706,6 +1912,7 @@ func invoiceWriteMergeItems(ctx context.Context, data []byte, spans map[string]i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
original[s.node.parent.path] = s.node.parent
|
original[s.node.parent.path] = s.node.parent
|
||||||
|
originalItems[s.node.parent.path] = item
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1732,7 +1939,7 @@ func invoiceWriteMergeItems(ctx context.Context, data []byte, spans map[string]i
|
|||||||
if path == "" {
|
if path == "" {
|
||||||
for _, f := range item.Fields {
|
for _, f := range item.Fields {
|
||||||
mapped := invoiceItemField(&InvoiceItem{}, value.Kind, f.Name)
|
mapped := invoiceItemField(&InvoiceItem{}, value.Kind, f.Name)
|
||||||
if value.Standard == InvoiceStandardEInvoice || !invoiceWriteContains(names, f.Name) {
|
if value.Standard == InvoiceStandardEInvoice || value.Standard == invoiceEDRM || !invoiceWriteContains(names, f.Name) {
|
||||||
return nil, fmt.Errorf("unsupported new invoice item fact %s", f.Name)
|
return nil, fmt.Errorf("unsupported new invoice item fact %s", f.Name)
|
||||||
}
|
}
|
||||||
if mapped != nil {
|
if mapped != nil {
|
||||||
@@ -1763,6 +1970,29 @@ func invoiceWriteMergeItems(ctx context.Context, data []byte, spans map[string]i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
n := original[path]
|
n := original[path]
|
||||||
|
if value.Standard == invoiceEDRM {
|
||||||
|
previous := originalItems[path]
|
||||||
|
if err := invoiceWriteUnmapped(previous.Fields, item.Fields); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var retained []*invoiceXMLNode
|
||||||
|
for _, child := range targets[i].children {
|
||||||
|
field := "/eInvoice/GoodsInfos/GoodsInfo/" + child.name.Local
|
||||||
|
before, _ := invoiceEDRMField(&old, &previous, field)
|
||||||
|
after, _ := invoiceEDRMField(&value, &item, field)
|
||||||
|
if before != nil && after != nil && *before == *after {
|
||||||
|
children := invoiceChildren(n, invoiceEDRM, child.name.Local)
|
||||||
|
if len(children) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(children) == 1 {
|
||||||
|
child = children[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retained = append(retained, child)
|
||||||
|
}
|
||||||
|
targets[i].children = retained
|
||||||
|
}
|
||||||
ps, err := invoiceWriteMergeChildren(data, spans, n, targets[i], space, names)
|
ps, err := invoiceWriteMergeChildren(data, spans, n, targets[i], space, names)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
Reference in new issue
Block a user