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