Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
45c7250831 | |||
f329652444 | |||
721c4ef2e0 | |||
fd48c5dc76 |
18
go.mod
18
go.mod
@ -1,6 +1,8 @@
|
||||
module penahub.gitlab.yandexcloud.net/backend/templategen
|
||||
|
||||
go 1.20
|
||||
go 1.21.0
|
||||
|
||||
toolchain go1.23.4
|
||||
|
||||
require (
|
||||
github.com/danilsolovyov/doc-template v0.0.0-20230327151707-b8182a1ee9f4
|
||||
@ -10,7 +12,7 @@ require (
|
||||
github.com/gorilla/schema v1.2.0
|
||||
github.com/minio/minio-go/v7 v7.0.61
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33
|
||||
github.com/twmb/franz-go v1.14.3
|
||||
go.mongodb.org/mongo-driver v1.12.1
|
||||
@ -25,6 +27,7 @@ require (
|
||||
cloud.google.com/go/compute v1.23.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/beevik/etree v1.5.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.10.0 // indirect
|
||||
@ -38,6 +41,7 @@ require (
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.16.7 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/lukasjarosch/go-docx v0.5.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
@ -61,11 +65,11 @@ require (
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
golang.org/x/crypto v0.12.0 // indirect
|
||||
golang.org/x/net v0.14.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 // indirect
|
||||
google.golang.org/grpc v1.57.0 // indirect
|
||||
|
29
go.sum
29
go.sum
@ -8,6 +8,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/beevik/etree v1.5.0 h1:iaQZFSDS+3kYZiGoc9uKeOkUY3nYMXOKLl6KIJxiJWs=
|
||||
github.com/beevik/etree v1.5.0/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs=
|
||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@ -93,6 +95,8 @@ github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/lukasjarosch/go-docx v0.5.0 h1:4vU+gJ4WMdqwRvRVFF+XMw3rPfUGSXlToPJIX3mHQsQ=
|
||||
github.com/lukasjarosch/go-docx v0.5.0/go.mod h1:ka/NZgDIJId48vMvcfWfduVTY7uV0/f8EgsmCjuS9X0=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
@ -138,8 +142,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33 h1:N9f/Q+2Ssa+yDcbfaoLTYvXmdeyUUxsJKdPUVsjSmiA=
|
||||
github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33/go.mod h1:rpcH99JknBh8seZmlOlUg51gasZH6QH34oXNsIwYT6E=
|
||||
github.com/twmb/franz-go v1.14.3 h1:cq8rxAnVYU1uF3SRVn8eEaUf+AaXKWlB0Cl3Ca7JSa4=
|
||||
@ -176,8 +180,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
@ -192,12 +196,13 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200925080053-05aa5d4ee321/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
|
||||
@ -207,8 +212,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -226,8 +231,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -237,8 +242,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
|
@ -1,12 +1,15 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/beevik/etree"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"penahub.gitlab.yandexcloud.net/backend/templategen/tools"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -33,22 +36,29 @@ type RespGenerated struct {
|
||||
|
||||
// ReqGeneratorService is the request we get on GeneratorService endpoint.
|
||||
type ReqGeneratorService struct {
|
||||
DocNumber string `json:"docnumber"`
|
||||
Date string `json:"date"`
|
||||
OrgTaxNum string `json:"orgtaxnum"`
|
||||
OrgName string `json:"orgname"`
|
||||
Name string `json:"name"`
|
||||
Amount string `json:"amount"`
|
||||
Price string `json:"price"`
|
||||
Sum string `json:"sum"`
|
||||
Unit string `json:"unit"` // default value set in NewReqGeneratorService()
|
||||
Tax string `json:"tax"` // default value set in NewReqGeneratorService()
|
||||
DocNumber string `json:"docnumber" xml:"DocNum"`
|
||||
Date string `json:"date" xml:"date"`
|
||||
OrgTaxNum string `json:"orgtaxnum" xml:"orgtaxnum"`
|
||||
OrgName string `json:"orgname" xml:"orgname"`
|
||||
Items []Item `json:"items" xml:"items>item"`
|
||||
TotalSum string `xml:"totalsum"`
|
||||
}
|
||||
|
||||
func NewReqGeneratorService() ReqGeneratorService {
|
||||
return ReqGeneratorService{
|
||||
Tax: "НДС не облагается",
|
||||
Unit: "-",
|
||||
type Item struct {
|
||||
Name string `json:"name" xml:"docnumber"`
|
||||
Amount string `json:"amount" xml:"amount"`
|
||||
Price string `json:"price" xml:"price"`
|
||||
Sum string `json:"sum" xml:"sum"`
|
||||
Unit string `json:"unit" xml:"unit"` // default value set in NewItem()
|
||||
Tax string `json:"tax" xml:"tax"` // default value set in NewItem()
|
||||
}
|
||||
|
||||
func (r *ReqGeneratorService) NewItem() {
|
||||
if r.Items != nil && len(r.Items) > 0 {
|
||||
for i, _ := range r.Items {
|
||||
r.Items[i].Tax = "НДС не облагается"
|
||||
r.Items[i].Unit = "-"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -382,7 +392,6 @@ func (h *Handlers) GeneratorService(writer http.ResponseWriter, req *http.Reques
|
||||
}
|
||||
|
||||
email := req.FormValue("email")
|
||||
|
||||
if email == "" {
|
||||
err = errors.New("email required")
|
||||
h.reportError(writer, http.StatusBadRequest, err)
|
||||
@ -390,21 +399,19 @@ func (h *Handlers) GeneratorService(writer http.ResponseWriter, req *http.Reques
|
||||
}
|
||||
|
||||
dataString := req.FormValue("data")
|
||||
|
||||
if dataString == "" {
|
||||
err = errors.New("data required")
|
||||
h.reportError(writer, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
// some default values filled
|
||||
data := NewReqGeneratorService()
|
||||
|
||||
// maybe need to check required fields and report errors if missing
|
||||
var data ReqGeneratorService
|
||||
if err = json.Unmarshal([]byte(dataString), &data); err != nil {
|
||||
h.reportError(writer, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
data.NewItem()
|
||||
|
||||
formFile, formFileHeader, err := req.FormFile("file")
|
||||
if err != nil {
|
||||
@ -423,8 +430,8 @@ func (h *Handlers) GeneratorService(writer http.ResponseWriter, req *http.Reques
|
||||
h.reportError(writer, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
resultFile, err := templategen.GenerateBytesFile(file, data)
|
||||
// TODO
|
||||
resultFile, err := GenerateBytesFile(file, data)
|
||||
if err != nil {
|
||||
h.reportError(writer, http.StatusBadRequest, err)
|
||||
return
|
||||
@ -442,6 +449,92 @@ func (h *Handlers) GeneratorService(writer http.ResponseWriter, req *http.Reques
|
||||
}
|
||||
}
|
||||
|
||||
func GenerateBytesFile(file []byte, data ReqGeneratorService) ([]byte, error) {
|
||||
// разархивируем документ чтобы содержимое поменять
|
||||
files, err := tools.ExtractDocx(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// надо менять только document.xml
|
||||
modifiedXML, err := modifyDocumentXML(files["word/document.xml"], data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files["word/document.xml"] = modifiedXML
|
||||
// заменив отредактированный - опять делаем докс файл
|
||||
return tools.CreateDocx(files)
|
||||
}
|
||||
|
||||
func modifyDocumentXML(xmlContent []byte, data ReqGeneratorService) ([]byte, error) {
|
||||
doc := etree.NewDocument()
|
||||
if err := doc.ReadFromBytes(xmlContent); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// базовые плейсхолдеры которые просто заменяются
|
||||
replaceMap := map[string]string{
|
||||
"DocNum": data.DocNumber,
|
||||
"Date": data.Date,
|
||||
"Orgname": data.OrgName,
|
||||
"INN": data.OrgTaxNum,
|
||||
"Total": data.TotalSum,
|
||||
}
|
||||
|
||||
for _, element := range doc.FindElements("//w:t") {
|
||||
if val, exists := replaceMap[element.Text()]; exists {
|
||||
element.SetText(val)
|
||||
}
|
||||
}
|
||||
// создание таблицы
|
||||
for _, element := range doc.FindElements("//w:t") {
|
||||
if element.Text() == "ItemsTable" {
|
||||
// родительские параметры находим
|
||||
paragraph := element.Parent().Parent()
|
||||
grandParent := paragraph.Parent()
|
||||
// запоминаем где был этот абзац
|
||||
prevElement := *paragraph
|
||||
// дропаем старый абзац
|
||||
grandParent.RemoveChild(paragraph)
|
||||
|
||||
// делаем таблицу
|
||||
table := etree.NewElement("w:tbl")
|
||||
tblPr := table.CreateElement("w:tblPr")
|
||||
tblPr.CreateElement("w:tblStyle").CreateAttr("w:val", "TableGrid")
|
||||
tblPr.CreateElement("w:tblW").CreateAttr("w:w", "5000")
|
||||
headerRow := table.CreateElement("w:tr")
|
||||
headers := []string{"№", "Наименование", "Кол-во", "Ед. изм.", "Цена за ед.", "Стоимость", "НДС"}
|
||||
for _, header := range headers {
|
||||
tools.CreateTableCell(headerRow, header, true)
|
||||
}
|
||||
|
||||
// заполняем данными
|
||||
for i, item := range data.Items {
|
||||
row := table.CreateElement("w:tr")
|
||||
tools.CreateTableCell(row, fmt.Sprintf("%d", i+1), false)
|
||||
tools.CreateTableCell(row, item.Name, false)
|
||||
tools.CreateTableCell(row, item.Amount, false)
|
||||
tools.CreateTableCell(row, item.Unit, false)
|
||||
tools.CreateTableCell(row, item.Price, false)
|
||||
tools.CreateTableCell(row, item.Sum, false)
|
||||
tools.CreateTableCell(row, item.Tax, false)
|
||||
}
|
||||
|
||||
// вставляем таблицу на место удаленного абзаца где был плейсхолдер ItemsTable
|
||||
grandParent.InsertChildAt(prevElement.Index(), table)
|
||||
prevElement = *table
|
||||
}
|
||||
}
|
||||
|
||||
doc.Indent(4)
|
||||
var buf bytes.Buffer
|
||||
if _, err := doc.WriteTo(&buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// generate - локальная функция, для генерации файла, возвращает ссылку для скачивания сгенерированного файла.
|
||||
func (h *Handlers) generate(r *http.Request, file, name, storageID, storageType,
|
||||
amoID string, history *model.History, data any) (string, error) {
|
||||
@ -461,23 +554,23 @@ func (h *Handlers) generate(r *http.Request, file, name, storageID, storageType,
|
||||
privilege, privilegeUnlimTimeExists := amoData.Privileges[model.PrivilegeTemplateUnlimTime]
|
||||
if privilegeUnlimTimeExists {
|
||||
if privilege.CreatedAt.AddDate(0, 0, int(privilege.Amount)).After(time.Now()) {
|
||||
privilegeUnlimTimeExists = false
|
||||
}
|
||||
privilegeUnlimTimeExists = false
|
||||
}
|
||||
}
|
||||
|
||||
if !privilegeUnlimTimeExists {
|
||||
// По количеству генераций
|
||||
privilege, privilegeCountExists := amoData.Privileges[model.PrivilegeTemplateCount]
|
||||
if privilegeCountExists {
|
||||
privilegeAmount = privilege.Amount
|
||||
if !privilegeUnlimTimeExists {
|
||||
// По количеству генераций
|
||||
privilege, privilegeCountExists := amoData.Privileges[model.PrivilegeTemplateCount]
|
||||
if privilegeCountExists {
|
||||
privilegeAmount = privilege.Amount
|
||||
|
||||
if privilegeAmount < 1 {
|
||||
return "", errors.New("tariff ended - not enough count")
|
||||
}
|
||||
} else {
|
||||
return "", errors.New("tariff ended - not enough count")
|
||||
}
|
||||
}
|
||||
if privilegeAmount < 1 {
|
||||
return "", errors.New("tariff ended - not enough count")
|
||||
}
|
||||
} else {
|
||||
return "", errors.New("tariff ended - not enough count")
|
||||
}
|
||||
}
|
||||
|
||||
var exportURL string
|
||||
history.Source.File = file
|
||||
|
@ -1,68 +1,91 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/danilsolovyov/doc-template/docx"
|
||||
"penahub.gitlab.yandexcloud.net/backend/templategen/templategen"
|
||||
)
|
||||
|
||||
func TestGeneratorService(t *testing.T) {
|
||||
file, err := os.ReadFile("../assets/TestGeneratorService.docx")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
//func TestGeneratorService(t *testing.T) {
|
||||
// file, err := os.ReadFile("../assets/TestGeneratorService.docx")
|
||||
// if err != nil {
|
||||
// t.Error(err)
|
||||
// }
|
||||
//
|
||||
// data := NewReqGeneratorService()
|
||||
//
|
||||
// // is filled by json.Unmarshal
|
||||
// data.DocNumber = "2"
|
||||
// data.Date = "13.05.2021"
|
||||
// data.OrgName = `ООО ЛИГА ХОДЬБЫ "ЖЕНЬШЕНЬ" ПРИМОРСКОГО КРАЯ`
|
||||
// data.OrgTaxNum = "999888111"
|
||||
// data.Sum = "60 000 руб."
|
||||
// data.Name = "Консультационные услуги"
|
||||
// data.Amount = "2"
|
||||
// data.Price = "1500"
|
||||
// data.Sum = "3000"
|
||||
//
|
||||
// resultBytes, err := templategen.GenerateBytesFile(file, data)
|
||||
// if err != nil {
|
||||
// t.Error(err)
|
||||
// }
|
||||
//
|
||||
// document := new(docx.Docx)
|
||||
// err = document.ReadBytes(resultBytes)
|
||||
// if err != nil {
|
||||
// t.Error(err)
|
||||
// }
|
||||
//
|
||||
// // _ = os.WriteFile("/tmp/out3.docx", resultBytes, 0644) // TODO delete
|
||||
//
|
||||
// checkAllFieldsPresent(t, document.GetContent(), data)
|
||||
//}
|
||||
//
|
||||
//func checkAllFieldsPresent(t *testing.T, content string, data interface{}) {
|
||||
// t.Helper()
|
||||
//
|
||||
// v := reflect.ValueOf(data)
|
||||
// vt := v.Type()
|
||||
//
|
||||
// for i := 0; i < v.NumField(); i++ {
|
||||
// field := v.Field(i)
|
||||
// fieldName := vt.Field(i).Name
|
||||
// fieldValue := field.Interface()
|
||||
//
|
||||
// switch x := fieldValue.(type) {
|
||||
// case string:
|
||||
// if !strings.Contains(content, x) {
|
||||
// t.Errorf("missing value for '%s': '%s'", fieldName, x)
|
||||
// }
|
||||
// default:
|
||||
// t.Error("unhandled field")
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
func Test_GenerateBytesFile(t *testing.T) {
|
||||
data := ReqGeneratorService{
|
||||
DocNumber: "123",
|
||||
Date: "2023-10-05",
|
||||
OrgTaxNum: "1234567890",
|
||||
OrgName: "ООО 'Пример'",
|
||||
Items: []Item{
|
||||
{Name: "Услуга 1", Amount: "1", Price: "1000", Sum: "1000"},
|
||||
{Name: "Услуга 2", Amount: "2", Price: "500", Sum: "1000"},
|
||||
},
|
||||
TotalSum: "5000",
|
||||
}
|
||||
|
||||
data := NewReqGeneratorService()
|
||||
data.NewItem()
|
||||
|
||||
// is filled by json.Unmarshal
|
||||
data.DocNumber = "2"
|
||||
data.Date = "13.05.2021"
|
||||
data.OrgName = `ООО ЛИГА ХОДЬБЫ "ЖЕНЬШЕНЬ" ПРИМОРСКОГО КРАЯ`
|
||||
data.OrgTaxNum = "999888111"
|
||||
data.Sum = "60 000 руб."
|
||||
data.Name = "Консультационные услуги"
|
||||
data.Amount = "2"
|
||||
data.Price = "1500"
|
||||
data.Sum = "3000"
|
||||
fmt.Println(data)
|
||||
|
||||
resultBytes, err := templategen.GenerateBytesFile(file, data)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
file, err := os.ReadFile("../static/examples/docx/report.docx")
|
||||
assert.NoError(t, err)
|
||||
genFileBytes, err := GenerateBytesFile(file, data)
|
||||
assert.NoError(t, err)
|
||||
|
||||
document := new(docx.Docx)
|
||||
err = document.ReadBytes(resultBytes)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// _ = os.WriteFile("/tmp/out3.docx", resultBytes, 0644) // TODO delete
|
||||
|
||||
checkAllFieldsPresent(t, document.GetContent(), data)
|
||||
}
|
||||
|
||||
func checkAllFieldsPresent(t *testing.T, content string, data interface{}) {
|
||||
t.Helper()
|
||||
|
||||
v := reflect.ValueOf(data)
|
||||
vt := v.Type()
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := v.Field(i)
|
||||
fieldName := vt.Field(i).Name
|
||||
fieldValue := field.Interface()
|
||||
|
||||
switch x := fieldValue.(type) {
|
||||
case string:
|
||||
if !strings.Contains(content, x) {
|
||||
t.Errorf("missing value for '%s': '%s'", fieldName, x)
|
||||
}
|
||||
default:
|
||||
t.Error("unhandled field")
|
||||
}
|
||||
}
|
||||
err = os.WriteFile("testGenBytesFile.docx", genFileBytes, 0664)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
BIN
static/examples/docx/report.docx
Normal file
BIN
static/examples/docx/report.docx
Normal file
Binary file not shown.
72
tools/xml_doc.go
Normal file
72
tools/xml_doc.go
Normal file
@ -0,0 +1,72 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"github.com/beevik/etree"
|
||||
"io"
|
||||
)
|
||||
|
||||
func ExtractDocx(file []byte) (map[string][]byte, error) {
|
||||
archive, err := zip.NewReader(bytes.NewReader(file), int64(len(file)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
files := make(map[string][]byte)
|
||||
|
||||
for _, f := range archive.File {
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
content, err := io.ReadAll(rc)
|
||||
rc.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files[f.Name] = content
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func CreateDocx(files map[string][]byte) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
zipWr := zip.NewWriter(&buf)
|
||||
|
||||
for name, content := range files {
|
||||
file, err := zipWr.Create(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = file.Write(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err := zipWr.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func CreateTableCell(row *etree.Element, text string, fat bool) {
|
||||
cell := row.CreateElement("w:tc")
|
||||
tcPr := cell.CreateElement("w:tcPr")
|
||||
tcPr.CreateElement("w:tcW").CreateAttr("w:w", "1000")
|
||||
|
||||
para := cell.CreateElement("w:p")
|
||||
run := para.CreateElement("w:r")
|
||||
|
||||
if fat {
|
||||
rPr := run.CreateElement("w:rPr")
|
||||
rPr.CreateElement("w:b")
|
||||
}
|
||||
|
||||
run.CreateElement("w:t").SetText(text)
|
||||
cell.AddChild(para)
|
||||
}
|
Loading…
Reference in New Issue
Block a user