mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 13:56:32 +08:00
1 parent
68d6a19656
commit
6639653843
6 files changed
+106
-44
No files matched your search
@@ -5857,7 +5857,6 @@ async function newInvoiceDraft() {
|
||||
|
||||
function renderInvoiceBasic(container, draft) {
|
||||
const fields = (draft.detail.fields || []).filter(field => field.item < 0 && !field.field.startsWith("facts.") && !field.field.startsWith("items."));
|
||||
const standards = {"http://www.edrm.org.cn/schema/e-invoice/2019": "电子发票/2019", "http://www.ofdspec.org/2016": "发票标引"};
|
||||
const labels = {
|
||||
kind: "票种", standard: "规范", version: "版本", number: "号码", code: "代码", date: "日期",
|
||||
currency: "币种", status: "状态", amount: "金额", taxAmount: "税额", totalAmount: "合计", remarks: "备注",
|
||||
@@ -5871,7 +5870,7 @@ function renderInvoiceBasic(container, draft) {
|
||||
container.append(title, grid);
|
||||
for (const field of fields.filter(field => field.group === group)) {
|
||||
const value = invoiceValue(draft.invoice, field.field);
|
||||
const display = field.field === "standard" ? standards[value] || value : value;
|
||||
const display = field.field === "standard" ? draft.detail.standardLabel || value : value;
|
||||
invoiceInput(grid, labels[field.field.split(".").at(-1)] || field.label, `Field_${field.field.replaceAll(".", "_")}`, display, value => {
|
||||
invoiceSetValue(draft.invoice, field.field, -1, value);
|
||||
invoiceDirty(draft, "model");
|
||||
|
||||
@@ -1004,6 +1004,13 @@ func documentInvoice(args []js.Value) (any, error) {
|
||||
return nil, fmt.Errorf("invoice index out of range")
|
||||
}
|
||||
value := report.Invoices[request.Index]
|
||||
standardLabel := value.Standard
|
||||
switch value.Standard {
|
||||
case ofdgo.InvoiceStandardEDRM2019:
|
||||
standardLabel = "电子发票/2019"
|
||||
case ofdgo.InvoiceStandardObjectReference:
|
||||
standardLabel = "发票标引"
|
||||
}
|
||||
capabilities, err := r.InvoiceCapabilities(ctx, request.Source, request.Index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1012,7 +1019,7 @@ func documentInvoice(args []js.Value) (any, error) {
|
||||
if bindingErr != nil {
|
||||
report.Issues = append(report.Issues, ofdgo.InvoiceIssue{Code: "binding-error", Message: bindingErr.Error(), Source: request.Source})
|
||||
}
|
||||
return map[string]any{"invoice": value, "fields": ofdgo.InvoiceFields(value), "capabilities": capabilities, "bindings": bindings, "digest": ofdgo.InvoiceDigest(data), "xml": string(data), "issues": report.Issues}, nil
|
||||
return map[string]any{"invoice": value, "standardLabel": standardLabel, "fields": ofdgo.InvoiceFields(value), "capabilities": capabilities, "bindings": bindings, "digest": ofdgo.InvoiceDigest(data), "xml": string(data), "issues": report.Issues}, nil
|
||||
}
|
||||
|
||||
// exportDocumentInvoice 导出发票原文
|
||||
|
||||
@@ -992,7 +992,7 @@ func invoicePayloadSignature(ctx context.Context, data []byte) (bool, error) {
|
||||
}
|
||||
node := nodes[len(nodes)-1]
|
||||
nodes = nodes[:len(nodes)-1]
|
||||
if node.name.Space == "http://www.w3.org/2000/09/xmldsig#" && node.name.Local == "Signature" || root.name.Space == "" && root.name.Local == "EInvoice" && node.parent == root && node.name.Space == "" && node.name.Local == "TaxBureauSignature" {
|
||||
if node.name.Space == invoiceXMLSignature && node.name.Local == "Signature" || root.name.Space == "" && root.name.Local == "EInvoice" && node.parent == root && node.name.Space == "" && node.name.Local == "TaxBureauSignature" {
|
||||
return true, nil
|
||||
}
|
||||
nodes = append(nodes, node.children...)
|
||||
|
||||
+10
-2
@@ -34,7 +34,7 @@ func InvoiceDigest(data []byte) string {
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
// ReadInvoice 读取当前文档及版本声明的发票,标引按对象引用提取文本
|
||||
// ReadInvoice 读取当前文档及版本声明的发票,字段路径相对导出XML,标引按对象引用提取文本
|
||||
// 入参: ctx 取消上下文, source 精确来源
|
||||
// 返回: InvoiceReport 发票及诊断, error 来源或读取错误
|
||||
func (r *Reader) ReadInvoice(ctx context.Context, source InvoiceSource) (InvoiceReport, error) {
|
||||
@@ -43,7 +43,12 @@ func (r *Reader) ReadInvoice(ctx context.Context, source InvoiceSource) (Invoice
|
||||
if err != nil {
|
||||
return InvoiceReport{}, err
|
||||
}
|
||||
report, err := r.readInvoiceXML(ctx, data, source)
|
||||
local := source
|
||||
local.NodePath = ""
|
||||
report, err := r.readInvoiceXML(ctx, data, local)
|
||||
for i := range report.Invoices {
|
||||
report.Invoices[i].Sources = []InvoiceSource{source}
|
||||
}
|
||||
report.DocumentIndex = r.DocumentIndex()
|
||||
if r.versionInfo != nil {
|
||||
report.Version = r.versionInfo.ID
|
||||
@@ -64,6 +69,9 @@ func (r *Reader) readInvoiceXML(ctx context.Context, data []byte, source Invoice
|
||||
return report, err
|
||||
}
|
||||
if root.name.Space != ofdNamespace2016 || root.name.Local != "root" || !invoiceObjectReferences(root) {
|
||||
if root.name.Local != "eInvoice" && root.name.Local != "EInvoice" && root.name.Local != "xbrl" {
|
||||
return InvoiceReport{}, nil
|
||||
}
|
||||
return report, nil
|
||||
}
|
||||
structure, err := parseStructure(ctx, data, CustomTag{TypeID: structureTypeOfficial, NameSpace: ofdNamespace2016})
|
||||
|
||||
+25
-24
@@ -117,7 +117,7 @@ type invoiceXMLPatch struct {
|
||||
func InvoiceFields(value Invoice) []InvoiceFieldDescriptor {
|
||||
format, supported := invoiceWriteFormat(value)
|
||||
probe := Invoice{Items: []InvoiceItem{{}}}
|
||||
if value.Standard == "EInvoice" {
|
||||
if value.Standard == InvoiceStandardEInvoice {
|
||||
probe.Number, probe.Date = "editable", "editable"
|
||||
for _, group := range invoicePlainGroups() {
|
||||
for _, name := range strings.Fields(group.value) {
|
||||
@@ -171,7 +171,7 @@ func InvoiceFields(value Invoice) []InvoiceFieldDescriptor {
|
||||
case "amount", "taxAmount", "totalAmount", "items.quantity", "items.unitPrice", "items.amount", "items.taxAmount":
|
||||
d.Type = "decimal"
|
||||
case "items.taxRate":
|
||||
if value.Standard == "EInvoice" {
|
||||
if value.Standard == InvoiceStandardEInvoice {
|
||||
d.Type = "decimal"
|
||||
}
|
||||
}
|
||||
@@ -208,13 +208,14 @@ func InvoiceFields(value Invoice) []InvoiceFieldDescriptor {
|
||||
// InvoiceFormats 返回独立格式能力副本,创建及编辑能力按业务版本分别声明
|
||||
// 返回: 已适配格式列表
|
||||
func InvoiceFormats() []InvoiceFormat {
|
||||
result := []InvoiceFormat{{Kind: "einv", Standard: "EInvoice", 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: ofdNamespace2016, Version: "1.0", Read: true})
|
||||
for _, kind := range []string{"inv", "einv", "atr", "rai"} {
|
||||
result = append(result, InvoiceFormat{Kind: kind, Standard: "http://xbrl.mof.gov.cn/taxonomy/2023-12-31/" + kind, Version: "2023-12-31", Read: true, Create: true, Edit: true, Items: kind == "inv" || kind == "atr"})
|
||||
for _, standard := range []string{InvoiceStandardVAT2023, InvoiceStandardDigital2023, InvoiceStandardAir2023, InvoiceStandardRail2023} {
|
||||
kind, version := invoiceTaxonomy(standard)
|
||||
result = append(result, InvoiceFormat{Kind: kind, Standard: standard, Version: version, Read: true, Create: true, Edit: true, Items: kind == "inv" || kind == "atr"})
|
||||
}
|
||||
return append(result, InvoiceFormat{Kind: "atr", Standard: "http://xbrl.mof.gov.cn/taxonomy/2021-11-30/atr", Version: "2021-11-30", Read: true, Edit: true, Items: true})
|
||||
return append(result, InvoiceFormat{Kind: "atr", Standard: InvoiceStandardAir2021, Version: "2021-11-30", Read: true, Edit: true, Items: true}, InvoiceFormat{Kind: "inv", Standard: InvoiceStandardVAT2021, Version: "2021-09-30", Read: true, Items: true})
|
||||
}
|
||||
|
||||
// DecodeInvoices 提取独立XML或XBRL中的发票,不执行联网或完整XSD验证
|
||||
@@ -280,7 +281,7 @@ func ValidateInvoice(ctx context.Context, value Invoice) ([]InvoiceIssue, error)
|
||||
add("invalid-date", "Date", "invalid invoice date")
|
||||
}
|
||||
}
|
||||
if value.Currency != "" && !invoiceCurrencyUnit("{http://www.xbrl.org/2003/iso4217}"+value.Currency) {
|
||||
if value.Currency != "" && !invoiceCurrencyUnit("{"+InvoiceCurrencyNamespace+"}"+value.Currency) {
|
||||
add("invalid-value", "Currency", "invalid ISO currency code")
|
||||
}
|
||||
for _, field := range []invoiceWriteField{{"Amount", value.Amount}, {"TaxAmount", value.TaxAmount}, {"TotalAmount", value.TotalAmount}} {
|
||||
@@ -303,11 +304,11 @@ func ValidateInvoice(ctx context.Context, value Invoice) ([]InvoiceIssue, error)
|
||||
add("invalid-decimal", fmt.Sprintf("Items[%d].%s", i, field.name), "invalid decimal lexical value")
|
||||
}
|
||||
}
|
||||
if value.Standard == "EInvoice" && item.TaxRate != "" && invoiceDecimal(item.TaxRate) == nil {
|
||||
if value.Standard == InvoiceStandardEInvoice && item.TaxRate != "" && invoiceDecimal(item.TaxRate) == nil {
|
||||
add("invalid-decimal", fmt.Sprintf("Items[%d].TaxRate", i), "invalid decimal lexical value")
|
||||
}
|
||||
}
|
||||
if value.Kind == "inv" || value.Standard == "EInvoice" {
|
||||
if value.Kind == "inv" || value.Standard == InvoiceStandardEInvoice {
|
||||
for _, total := range []invoiceWriteField{{"Amount", value.Amount}, {"TaxAmount", value.TaxAmount}} {
|
||||
sum := new(big.Rat)
|
||||
complete := len(value.Items) > 0
|
||||
@@ -459,7 +460,7 @@ func EncodeInvoiceWithOptions(ctx context.Context, value Invoice, options Invoic
|
||||
return nil, err
|
||||
}
|
||||
var data []byte
|
||||
if value.Standard == "EInvoice" {
|
||||
if value.Standard == InvoiceStandardEInvoice {
|
||||
data, err = invoiceEncodePlain(value)
|
||||
if err == nil {
|
||||
data, err = invoiceWritePlainSections(ctx, data, options)
|
||||
@@ -621,7 +622,7 @@ func invoiceEncodeXBRL(value Invoice, options InvoiceEncodingOptions) ([]byte, e
|
||||
}
|
||||
var b strings.Builder
|
||||
b.WriteString(xml.Header)
|
||||
fmt.Fprintf(&b, `<xbrli:xbrl xmlns:xbrli="%s" xmlns:xsi="%s" xmlns:iso4217="http://www.xbrl.org/2003/iso4217" xmlns:f="%s" xmlns:link="http://www.xbrl.org/2003/linkbase" xmlns:xlink="http://www.w3.org/1999/xlink">`, invoiceXBRL, invoiceXSI, value.Standard)
|
||||
fmt.Fprintf(&b, `<xbrli:xbrl xmlns:xbrli="%s" xmlns:xsi="%s" xmlns:iso4217="%s" xmlns:f="%s" xmlns:link="%s" xmlns:xlink="%s">`, invoiceXBRL, invoiceXSI, InvoiceCurrencyNamespace, value.Standard, invoiceLinkbase, invoiceXLink)
|
||||
fmt.Fprintf(&b, `<link:schemaRef xlink:type="simple" xlink:href="%s"/><xbrli:context id="%s"><xbrli:entity><xbrli:identifier scheme="%s">%s</xbrli:identifier></xbrli:entity><xbrli:period>`, invoiceWriteEscape(options.SchemaRef), options.Context.ID, invoiceWriteEscape(options.Context.Scheme), invoiceWriteEscape(options.Context.Identifier))
|
||||
if options.Context.Instant != "" {
|
||||
if options.Context.StartDate != "" || options.Context.EndDate != "" {
|
||||
@@ -722,7 +723,7 @@ func invoiceWriteFact(b *strings.Builder, value Invoice, fact InvoiceFact, optio
|
||||
if numeric {
|
||||
unit := ""
|
||||
for _, u := range options.Units {
|
||||
match := u.Namespace == "http://www.xbrl.org/2003/iso4217" && u.Name == value.Currency
|
||||
match := u.Namespace == InvoiceCurrencyNamespace && u.Name == value.Currency
|
||||
if !invoiceMonetary(value.Kind, fact.Name) {
|
||||
match = u.Namespace == invoiceXBRL && u.Name == "pure"
|
||||
if fact.Unit != "" {
|
||||
@@ -1215,7 +1216,7 @@ func UpdateInvoiceXML(ctx context.Context, data []byte, invoiceIndex int, value
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if value.Standard == "EInvoice" {
|
||||
if value.Standard == InvoiceStandardEInvoice {
|
||||
var expanded []invoiceXMLPatch
|
||||
for _, s := range spans {
|
||||
n := s.node
|
||||
@@ -1251,7 +1252,7 @@ func UpdateInvoiceXML(ctx context.Context, data []byte, invoiceIndex int, value
|
||||
clean.Facts = nil
|
||||
clean.Sources = nil
|
||||
clean.Items = append([]InvoiceItem(nil), value.Items...)
|
||||
if clean.Standard != "EInvoice" {
|
||||
if clean.Standard != InvoiceStandardEInvoice {
|
||||
for _, name := range invoiceWriteHeader(clean) {
|
||||
previous := invoiceXMLField(&old, old.Kind, name)
|
||||
target := invoiceXMLField(&clean, clean.Kind, name)
|
||||
@@ -1262,7 +1263,7 @@ func UpdateInvoiceXML(ctx context.Context, data []byte, invoiceIndex int, value
|
||||
}
|
||||
for i := range clean.Items {
|
||||
clean.Items[i].Fields = nil
|
||||
if value.Standard != "EInvoice" && (value.Kind == "atr" || value.Kind == "inv") {
|
||||
if value.Standard != InvoiceStandardEInvoice && (value.Kind == "atr" || value.Kind == "inv") {
|
||||
for _, f := range value.Items[i].Fields {
|
||||
if !invoiceWriteContains(invoiceWriteItemNames(value.Kind), f.Name) {
|
||||
continue
|
||||
@@ -1291,7 +1292,7 @@ func UpdateInvoiceXML(ctx context.Context, data []byte, invoiceIndex int, value
|
||||
}
|
||||
}
|
||||
var generated []byte
|
||||
if value.Standard == "EInvoice" {
|
||||
if value.Standard == InvoiceStandardEInvoice {
|
||||
generated, err = invoiceEncodePlain(clean)
|
||||
} else {
|
||||
options, e := invoiceWriteOriginalOptions(root, old)
|
||||
@@ -1310,7 +1311,7 @@ func UpdateInvoiceXML(ctx context.Context, data []byte, invoiceIndex int, value
|
||||
var patches []invoiceXMLPatch
|
||||
var trailingGroups []invoiceXMLPatch
|
||||
var itemParent, generatedParent *invoiceXMLNode
|
||||
if value.Standard == "EInvoice" {
|
||||
if value.Standard == InvoiceStandardEInvoice {
|
||||
if len(invoiceChildren(root, "", "EInvoiceData")) != 1 || len(invoiceChildren(root, "", "TaxSupervisionInfo")) != 1 {
|
||||
return nil, fmt.Errorf("invoice requires unique data and supervision groups")
|
||||
}
|
||||
@@ -1425,7 +1426,7 @@ func UpdateInvoiceXML(ctx context.Context, data []byte, invoiceIndex int, value
|
||||
var matches []Invoice
|
||||
for _, inv := range after.Invoices {
|
||||
if inv.Kind == value.Kind && inv.Standard == value.Standard {
|
||||
if value.Standard == "EInvoice" {
|
||||
if value.Standard == InvoiceStandardEInvoice {
|
||||
matches = append(matches, inv)
|
||||
continue
|
||||
}
|
||||
@@ -1503,8 +1504,8 @@ func invoiceWriteOriginalOptions(root *invoiceXMLNode, value Invoice) (InvoiceEn
|
||||
}
|
||||
}
|
||||
for _, n := range root.children {
|
||||
if n.name == (xml.Name{Space: "http://www.xbrl.org/2003/linkbase", Local: "schemaRef"}) && options.SchemaRef == "" {
|
||||
options.SchemaRef = invoiceXMLAttr(n, "http://www.w3.org/1999/xlink", "href")
|
||||
if n.name == (xml.Name{Space: invoiceLinkbase, Local: "schemaRef"}) && options.SchemaRef == "" {
|
||||
options.SchemaRef = invoiceXMLAttr(n, invoiceXLink, "href")
|
||||
}
|
||||
if n.name == (xml.Name{Space: invoiceXBRL, Local: "context"}) && invoiceXMLAttr(n, "", "id") == ref {
|
||||
dims, ok := invoiceContext(n)
|
||||
@@ -1684,7 +1685,7 @@ func invoiceWriteMergeItems(ctx context.Context, data []byte, spans map[string]i
|
||||
}
|
||||
space, name := value.Standard, invoiceWriteTuple(value.Kind)
|
||||
names := invoiceWriteItemNames(value.Kind)
|
||||
if value.Standard == "EInvoice" {
|
||||
if value.Standard == InvoiceStandardEInvoice {
|
||||
space = ""
|
||||
name = "IssuItemInformation"
|
||||
names = strings.Fields("ItemName SpecMod MeaUnits Quantity UnPrice Amount TaxRate ComTaxAm")
|
||||
@@ -1731,7 +1732,7 @@ func invoiceWriteMergeItems(ctx context.Context, data []byte, spans map[string]i
|
||||
if path == "" {
|
||||
for _, f := range item.Fields {
|
||||
mapped := invoiceItemField(&InvoiceItem{}, value.Kind, f.Name)
|
||||
if value.Standard == "EInvoice" || !invoiceWriteContains(names, f.Name) {
|
||||
if value.Standard == InvoiceStandardEInvoice || !invoiceWriteContains(names, f.Name) {
|
||||
return nil, fmt.Errorf("unsupported new invoice item fact %s", f.Name)
|
||||
}
|
||||
if mapped != nil {
|
||||
@@ -1793,7 +1794,7 @@ func invoiceWriteMergeItems(ctx context.Context, data []byte, spans map[string]i
|
||||
if len(ordered) == 0 && len(combined) > 0 {
|
||||
var p invoiceXMLPatch
|
||||
var err error
|
||||
if value.Standard == "EInvoice" {
|
||||
if value.Standard == InvoiceStandardEInvoice {
|
||||
p, err = invoiceWriteOrderedInsert(data, spans, parent, "", name, strings.Fields("SellerInformation BuyerInformation BasicInformation IssuItemInformation SpecificInformation AdditionalInformation"), combined)
|
||||
} else {
|
||||
p, err = invoiceWriteInsert(spans[parent.path], combined, data)
|
||||
@@ -1837,7 +1838,7 @@ func invoiceWriteOrderedInsert(data []byte, spans map[string]invoiceXMLSpan, par
|
||||
// 入参: value 发票
|
||||
// 返回: 缺少元组字段错误
|
||||
func invoiceWriteCreation(value Invoice) error {
|
||||
if value.Standard == "EInvoice" {
|
||||
if value.Standard == InvoiceStandardEInvoice {
|
||||
return nil
|
||||
}
|
||||
for i, item := range value.Items {
|
||||
|
||||
+61
-14
@@ -28,14 +28,59 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// invoiceXBRL 表示XBRL实例命名空间
|
||||
const invoiceXBRL = "http://www.xbrl.org/2003/instance"
|
||||
// invoiceXMLNamespace 表示XML预定义命名空间
|
||||
const invoiceXMLNamespace = "http://www.w3.org/XML/1998/namespace"
|
||||
|
||||
// invoiceXSI 表示XMLSchema实例命名空间
|
||||
const invoiceXSI = "http://www.w3.org/2001/XMLSchema-instance"
|
||||
|
||||
// invoiceXLink 表示XML链接命名空间
|
||||
const invoiceXLink = "http://www.w3.org/1999/xlink"
|
||||
|
||||
// invoiceXMLSignature 表示XML数字签名命名空间
|
||||
const invoiceXMLSignature = "http://www.w3.org/2000/09/xmldsig#"
|
||||
|
||||
// invoiceXBRL 表示XBRL实例命名空间
|
||||
const invoiceXBRL = "http://www.xbrl.org/2003/instance"
|
||||
|
||||
// invoiceLinkbase 表示XBRL链接库命名空间
|
||||
const invoiceLinkbase = "http://www.xbrl.org/2003/linkbase"
|
||||
|
||||
// invoiceDimension 表示XBRL实例维度命名空间
|
||||
const invoiceDimension = "http://xbrl.org/2006/xbrldi"
|
||||
|
||||
// InvoiceCurrencyNamespace 表示InvoiceUnit中ISO货币度量使用的命名空间
|
||||
const InvoiceCurrencyNamespace = "http://www.xbrl.org/2003/iso4217"
|
||||
|
||||
// InvoiceStandardVAT2023 表示2023版增值税发票分类标准
|
||||
const InvoiceStandardVAT2023 = "http://xbrl.mof.gov.cn/taxonomy/2023-12-31/inv"
|
||||
|
||||
// InvoiceStandardDigital2023 表示2023版数字化电子发票分类标准
|
||||
const InvoiceStandardDigital2023 = "http://xbrl.mof.gov.cn/taxonomy/2023-12-31/einv"
|
||||
|
||||
// InvoiceStandardAir2023 表示2023版航空运输电子客票行程单分类标准
|
||||
const InvoiceStandardAir2023 = "http://xbrl.mof.gov.cn/taxonomy/2023-12-31/atr"
|
||||
|
||||
// InvoiceStandardRail2023 表示2023版铁路电子客票分类标准
|
||||
const InvoiceStandardRail2023 = "http://xbrl.mof.gov.cn/taxonomy/2023-12-31/rai"
|
||||
|
||||
// InvoiceStandardVAT2021 表示2021版增值税发票分类标准,仅支持读取
|
||||
const InvoiceStandardVAT2021 = "http://xbrl.mof.gov.cn/taxonomy/2021-09-30/inv"
|
||||
|
||||
// InvoiceStandardAir2021 表示2021版航空运输电子客票行程单分类标准
|
||||
const InvoiceStandardAir2021 = "http://xbrl.mof.gov.cn/taxonomy/2021-11-30/atr"
|
||||
|
||||
// InvoiceStandardEInvoice 表示无命名空间的EInvoice业务标准,版本由Invoice.Version指定
|
||||
const InvoiceStandardEInvoice = "EInvoice"
|
||||
|
||||
// InvoiceStandardObjectReference 表示基于OFD对象引用的只读发票标引
|
||||
const InvoiceStandardObjectReference = ofdNamespace2016
|
||||
|
||||
// InvoiceStandardEDRM2019 表示2019版电子发票业务标准
|
||||
const InvoiceStandardEDRM2019 = "http://www.edrm.org.cn/schema/e-invoice/2019"
|
||||
|
||||
// invoiceEDRM 表示2019版电子发票业务命名空间
|
||||
const invoiceEDRM = "http://www.edrm.org.cn/schema/e-invoice/2019"
|
||||
const invoiceEDRM = InvoiceStandardEDRM2019
|
||||
|
||||
// invoiceDecimalPattern 仅接受XML十进制词法
|
||||
var invoiceDecimalPattern = regexp.MustCompile(`^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)$`)
|
||||
@@ -150,7 +195,7 @@ func invoiceXMLTree(ctx context.Context, data []byte) (*invoiceXMLNode, error) {
|
||||
return nil, fmt.Errorf("invoice XML exceeds namespace URI limit")
|
||||
}
|
||||
}
|
||||
n := &invoiceXMLNode{name: v.Name, attrs: v.Attr, parent: current, ns: map[string]string{"xml": "http://www.w3.org/XML/1998/namespace"}, counts: map[xml.Name]int{}, start: d.InputOffset()}
|
||||
n := &invoiceXMLNode{name: v.Name, attrs: v.Attr, parent: current, ns: map[string]string{"xml": invoiceXMLNamespace}, counts: map[xml.Name]int{}, start: d.InputOffset()}
|
||||
index := 1
|
||||
if current != nil {
|
||||
for k, v := range current.ns {
|
||||
@@ -223,16 +268,18 @@ func invoiceXMLTree(ctx context.Context, data []byte) (*invoiceXMLNode, error) {
|
||||
// 返回: 票种及版本,未知时为空
|
||||
func invoiceTaxonomy(ns string) (string, string) {
|
||||
switch ns {
|
||||
case "http://xbrl.mof.gov.cn/taxonomy/2023-12-31/inv":
|
||||
case InvoiceStandardVAT2023:
|
||||
return "inv", "2023-12-31"
|
||||
case "http://xbrl.mof.gov.cn/taxonomy/2023-12-31/einv":
|
||||
case InvoiceStandardDigital2023:
|
||||
return "einv", "2023-12-31"
|
||||
case "http://xbrl.mof.gov.cn/taxonomy/2023-12-31/atr":
|
||||
case InvoiceStandardAir2023:
|
||||
return "atr", "2023-12-31"
|
||||
case "http://xbrl.mof.gov.cn/taxonomy/2023-12-31/rai":
|
||||
case InvoiceStandardRail2023:
|
||||
return "rai", "2023-12-31"
|
||||
case "http://xbrl.mof.gov.cn/taxonomy/2021-11-30/atr":
|
||||
case InvoiceStandardAir2021:
|
||||
return "atr", "2021-11-30"
|
||||
case InvoiceStandardVAT2021:
|
||||
return "inv", "2021-09-30"
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
@@ -351,7 +398,7 @@ func parseInvoiceXML(ctx context.Context, data []byte, source InvoiceSource) (In
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
if n.name.Space == invoiceXBRL || n.name.Space == "http://www.xbrl.org/2003/linkbase" {
|
||||
if n.name.Space == invoiceXBRL || n.name.Space == invoiceLinkbase {
|
||||
return nil
|
||||
}
|
||||
if len(n.children) > 0 && invoiceXMLAttr(n, "", "contextRef") == "" {
|
||||
@@ -421,7 +468,7 @@ func parseInvoiceXML(ctx context.Context, data []byte, source InvoiceSource) (In
|
||||
return nil
|
||||
}
|
||||
if numeric && invoiceMonetary(kind, fact.Name) && invoiceCurrencyUnit(unitValues[fact.Unit]) {
|
||||
currency := strings.TrimPrefix(unitValues[fact.Unit], "{http://www.xbrl.org/2003/iso4217}")
|
||||
currency := strings.TrimPrefix(unitValues[fact.Unit], "{"+InvoiceCurrencyNamespace+"}")
|
||||
cf := fact
|
||||
cf.Value = currency
|
||||
cf.Unit = ""
|
||||
@@ -762,7 +809,7 @@ func invoiceContext(n *invoiceXMLNode) ([]InvoiceDimension, bool) {
|
||||
seen := map[xml.Name]bool{}
|
||||
var walk func(*invoiceXMLNode)
|
||||
walk = func(c *invoiceXMLNode) {
|
||||
if c.name.Space == "http://xbrl.org/2006/xbrldi" && (c.name.Local == "explicitMember" || c.name.Local == "typedMember") {
|
||||
if c.name.Space == invoiceDimension && (c.name.Local == "explicitMember" || c.name.Local == "typedMember") {
|
||||
name, ok := invoiceQName(c, invoiceXMLAttr(c, "", "dimension"))
|
||||
if !ok || name.Space == "" || seen[name] {
|
||||
valid = false
|
||||
@@ -905,7 +952,7 @@ func invoiceMonetary(kind, name string) bool {
|
||||
// 入参: unit 展开的单位表示
|
||||
// 返回: 是否为三位大写币种度量
|
||||
func invoiceCurrencyUnit(unit string) bool {
|
||||
const prefix = "{http://www.xbrl.org/2003/iso4217}"
|
||||
const prefix = "{" + InvoiceCurrencyNamespace + "}"
|
||||
if !strings.HasPrefix(unit, prefix) {
|
||||
return false
|
||||
}
|
||||
@@ -995,7 +1042,7 @@ func invoiceObjectReferences(root *invoiceXMLNode) bool {
|
||||
// 返回: 发票及诊断,取消错误
|
||||
func invoicePlainXML(ctx context.Context, root *invoiceXMLNode, source InvoiceSource) (InvoiceReport, error) {
|
||||
report := InvoiceReport{}
|
||||
inv := Invoice{Kind: "einv", Standard: "EInvoice", Version: "0.3", Sources: []InvoiceSource{source}}
|
||||
inv := Invoice{Kind: "einv", Standard: InvoiceStandardEInvoice, Version: "0.3", Sources: []InvoiceSource{source}}
|
||||
namespace, itemPath := "", "/EInvoice/EInvoiceData/IssuItemInformation"
|
||||
if root.name.Local == "eInvoice" {
|
||||
if invoiceXMLAttr(root, "", "Version") != "1.0" || len(invoiceChildren(root, invoiceEDRM, "InvoiceNo")) == 0 {
|
||||
|
||||
Reference in new issue
Block a user