Moving pdf attachments to go
This commit is contained in:
parent
a33eaa0c3c
commit
3841d961fa
BIN
go/bin/server
Executable file
BIN
go/bin/server
Executable file
Binary file not shown.
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
"code.springupsoftware.com/cmc/cmc-sales/internal/cmc/db"
|
||||
"code.springupsoftware.com/cmc/cmc-sales/internal/cmc/email"
|
||||
"code.springupsoftware.com/cmc/cmc-sales/internal/cmc/handlers"
|
||||
"code.springupsoftware.com/cmc/cmc-sales/internal/cmc/handlers/attachments"
|
||||
quotes "code.springupsoftware.com/cmc/cmc-sales/internal/cmc/handlers/quotes"
|
||||
"code.springupsoftware.com/cmc/cmc-sales/internal/cmc/templates"
|
||||
|
|
@ -84,6 +85,13 @@ func main() {
|
|||
goRouter.HandleFunc("/attachments/{id}", attachmentHandler.Get).Methods("GET")
|
||||
goRouter.HandleFunc("/attachments/{id}", attachmentHandler.Delete).Methods("DELETE")
|
||||
|
||||
// PDF generation endpoints - called from PHP app
|
||||
goRouter.HandleFunc("/pdf/generate-invoice", handlers.GenerateInvoicePDF).Methods("POST")
|
||||
goRouter.HandleFunc("/pdf/generate-quote", handlers.GenerateQuotePDF).Methods("POST")
|
||||
goRouter.HandleFunc("/pdf/generate-po", handlers.GeneratePurchaseOrderPDF).Methods("POST")
|
||||
goRouter.HandleFunc("/pdf/generate-packinglist", handlers.GeneratePackingListPDF).Methods("POST")
|
||||
goRouter.HandleFunc("/pdf/generate-orderack", handlers.GenerateOrderAckPDF).Methods("POST")
|
||||
|
||||
// The following routes are currently disabled:
|
||||
/*
|
||||
// API routes
|
||||
|
|
|
|||
42
go/go.mod
42
go/go.mod
|
|
@ -1,22 +1,56 @@
|
|||
module code.springupsoftware.com/cmc/cmc-sales
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.24.3
|
||||
|
||||
require (
|
||||
github.com/go-co-op/gocron v1.37.0
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/jhillyerd/enmime v1.3.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/jung-kurt/gofpdf v1.16.2
|
||||
golang.org/x/text v0.27.0
|
||||
golang.org/x/oauth2 v0.33.0
|
||||
google.golang.org/api v0.257.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-co-op/gocron v1.37.0 // indirect
|
||||
github.com/google/uuid v1.4.0 // indirect
|
||||
cloud.google.com/go/auth v0.17.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
|
||||
github.com/hhrutter/lzw v1.0.0 // indirect
|
||||
github.com/hhrutter/pkcs7 v0.2.0 // indirect
|
||||
github.com/hhrutter/tiff v1.0.2 // indirect
|
||||
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/image v0.32.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
|
||||
google.golang.org/grpc v1.77.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
|
|
|||
115
go/go.sum
115
go/go.sum
|
|
@ -1,21 +1,54 @@
|
|||
cloud.google.com/go/auth v0.16.3 h1:kabzoQ9/bobUmnseYnBO6qQG7q4a/CffFRlJSxv2wCc=
|
||||
cloud.google.com/go/auth v0.16.3/go.mod h1:NucRGjaXfzP1ltpcQ7On/VTZ0H4kWB5Jy+Y9Dnm76fA=
|
||||
cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4=
|
||||
cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
|
||||
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
|
||||
github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
|
||||
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
|
||||
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/hhrutter/lzw v1.0.0 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0=
|
||||
github.com/hhrutter/lzw v1.0.0/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo=
|
||||
github.com/hhrutter/pkcs7 v0.2.0 h1:i4HN2XMbGQpZRnKBLsUwO3dSckzgX142TNqY/KfXg+I=
|
||||
github.com/hhrutter/pkcs7 v0.2.0/go.mod h1:aEzKz0+ZAlz7YaEMY47jDHL14hVWD6iXt0AgqgAvWgE=
|
||||
github.com/hhrutter/tiff v1.0.2 h1:7H3FQQpKu/i5WaSChoD1nnJbGx4MxU5TlNqqpxw55z8=
|
||||
github.com/hhrutter/tiff v1.0.2/go.mod h1:pcOeuK5loFUE7Y/WnzGw20YxUdnqjY1P0Jlcieb/cCw=
|
||||
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA=
|
||||
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||
github.com/jhillyerd/enmime v1.3.0 h1:LV5kzfLidiOr8qRGIpYYmUZCnhrPbcFAnAFUnWn99rw=
|
||||
|
|
@ -28,9 +61,19 @@ github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/Ym
|
|||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/pdfcpu/pdfcpu v0.11.1 h1:htHBSkGH5jMKWC6e0sihBFbcKZ8vG1M67c8/dJxhjas=
|
||||
github.com/pdfcpu/pdfcpu v0.11.1/go.mod h1:pP3aGga7pRvwFWAm9WwFvo+V68DfANi9kxSQYioNYcw=
|
||||
github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
@ -42,7 +85,11 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
|||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
|
|
@ -51,23 +98,61 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
|||
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.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/image v0.32.0 h1:6lZQWq75h7L5IWNk0r+SCpUJ6tUVd3v4ZHnbRKLkUDQ=
|
||||
golang.org/x/image v0.32.0/go.mod h1:/R37rrQmKXtO6tYXAjtDLwQgFLHmhW+V6ayXlxzP2Pc=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
|
||||
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/api v0.257.0 h1:8Y0lzvHlZps53PEaw+G29SsQIkuKrumGWs9puiexNAA=
|
||||
google.golang.org/api v0.257.0/go.mod h1:4eJrr+vbVaZSqs7vovFd1Jb/A6ml6iw2e6FBYf3GAO4=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
||||
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
|
|
@ -386,7 +386,7 @@ func (h *PageHandler) EnquiriesIndex(w http.ResponseWriter, r *http.Request) {
|
|||
offset := (page - 1) * limit
|
||||
|
||||
var enquiries interface{}
|
||||
var err error
|
||||
var listErr error
|
||||
var hasMore bool
|
||||
|
||||
// Check if we want archived enquiries
|
||||
|
|
@ -401,6 +401,8 @@ func (h *PageHandler) EnquiriesIndex(w http.ResponseWriter, r *http.Request) {
|
|||
archivedEnquiries = archivedEnquiries[:limit]
|
||||
}
|
||||
enquiries = archivedEnquiries
|
||||
} else {
|
||||
listErr = err
|
||||
}
|
||||
} else {
|
||||
activeEnquiries, err := h.queries.ListEnquiries(r.Context(), db.ListEnquiriesParams{
|
||||
|
|
@ -413,11 +415,13 @@ func (h *PageHandler) EnquiriesIndex(w http.ResponseWriter, r *http.Request) {
|
|||
activeEnquiries = activeEnquiries[:limit]
|
||||
}
|
||||
enquiries = activeEnquiries
|
||||
} else {
|
||||
listErr = err
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
if listErr != nil {
|
||||
http.Error(w, listErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
453
go/internal/cmc/handlers/pdf_api.go
Normal file
453
go/internal/cmc/handlers/pdf_api.go
Normal file
|
|
@ -0,0 +1,453 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"code.springupsoftware.com/cmc/cmc-sales/internal/cmc/db"
|
||||
"code.springupsoftware.com/cmc/cmc-sales/internal/cmc/pdf"
|
||||
)
|
||||
|
||||
// InvoiceLineItemRequest is the JSON shape for a single line item.
|
||||
type InvoiceLineItemRequest struct {
|
||||
ItemNumber string `json:"item_number"`
|
||||
Quantity string `json:"quantity"`
|
||||
Title string `json:"title"`
|
||||
UnitPrice float64 `json:"unit_price"`
|
||||
TotalPrice float64 `json:"total_price"`
|
||||
}
|
||||
|
||||
// InvoicePDFRequest is the expected payload from the PHP app.
|
||||
type InvoicePDFRequest struct {
|
||||
DocumentID int32 `json:"document_id"`
|
||||
InvoiceTitle string `json:"invoice_title"`
|
||||
CustomerName string `json:"customer_name"`
|
||||
ContactEmail string `json:"contact_email"`
|
||||
ContactName string `json:"contact_name"`
|
||||
UserFirstName string `json:"user_first_name"`
|
||||
UserLastName string `json:"user_last_name"`
|
||||
UserEmail string `json:"user_email"`
|
||||
YourReference string `json:"your_reference"`
|
||||
ShipVia string `json:"ship_via"`
|
||||
FOB string `json:"fob"`
|
||||
IssueDate string `json:"issue_date"` // ISO date: 2006-01-02
|
||||
CurrencySymbol string `json:"currency_symbol"` // e.g. "$"
|
||||
ShowGST bool `json:"show_gst"`
|
||||
LineItems []InvoiceLineItemRequest `json:"line_items"`
|
||||
OutputDir string `json:"output_dir"` // optional override
|
||||
}
|
||||
|
||||
// GenerateInvoicePDF handles POST /api/pdf/invoice and writes a PDF to disk.
|
||||
// It returns JSON: {"filename":"<name>.pdf"}
|
||||
func GenerateInvoicePDF(w http.ResponseWriter, r *http.Request) {
|
||||
var req InvoicePDFRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid JSON payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.InvoiceTitle == "" || req.CustomerName == "" {
|
||||
http.Error(w, "invoice_title and customer_name are required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
issueDate := time.Now()
|
||||
if req.IssueDate != "" {
|
||||
if parsed, err := time.Parse("2006-01-02", req.IssueDate); err == nil {
|
||||
issueDate = parsed
|
||||
}
|
||||
}
|
||||
|
||||
outputDir := req.OutputDir
|
||||
if outputDir == "" {
|
||||
outputDir = os.Getenv("PDF_OUTPUT_DIR")
|
||||
}
|
||||
if outputDir == "" {
|
||||
outputDir = "../php/app/webroot/pdf"
|
||||
}
|
||||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||||
log.Printf("GenerateInvoicePDF: failed to create output dir: %v", err)
|
||||
http.Error(w, "failed to prepare output directory", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Map request into the existing PDF generation types.
|
||||
doc := &db.Document{ID: req.DocumentID}
|
||||
inv := &db.Invoice{Title: req.InvoiceTitle}
|
||||
cust := &db.Customer{Name: req.CustomerName}
|
||||
|
||||
lineItems := make([]db.GetLineItemsTableRow, len(req.LineItems))
|
||||
for i, li := range req.LineItems {
|
||||
lineItems[i] = db.GetLineItemsTableRow{
|
||||
ItemNumber: li.ItemNumber,
|
||||
Quantity: li.Quantity,
|
||||
Title: li.Title,
|
||||
GrossUnitPrice: sql.NullString{String: fmt.Sprintf("%.2f", li.UnitPrice), Valid: true},
|
||||
GrossPrice: sql.NullString{String: fmt.Sprintf("%.2f", li.TotalPrice), Valid: true},
|
||||
}
|
||||
}
|
||||
|
||||
data := &pdf.InvoicePDFData{
|
||||
Document: doc,
|
||||
Invoice: inv,
|
||||
Customer: cust,
|
||||
LineItems: lineItems,
|
||||
CurrencySymbol: req.CurrencySymbol,
|
||||
ShowGST: req.ShowGST,
|
||||
ShipVia: req.ShipVia,
|
||||
FOB: req.FOB,
|
||||
IssueDate: issueDate,
|
||||
EmailTo: req.ContactEmail,
|
||||
Attention: req.ContactName,
|
||||
FromName: fmt.Sprintf("%s %s", req.UserFirstName, req.UserLastName),
|
||||
FromEmail: req.UserEmail,
|
||||
YourReference: req.YourReference,
|
||||
}
|
||||
|
||||
filename, err := pdf.GenerateInvoicePDF(data, outputDir)
|
||||
if err != nil {
|
||||
log.Printf("GenerateInvoicePDF: failed to generate PDF: %v", err)
|
||||
http.Error(w, "failed to generate PDF", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{"filename": filename})
|
||||
}
|
||||
|
||||
// QuoteLineItemRequest reuses the invoice item shape
|
||||
type QuoteLineItemRequest = InvoiceLineItemRequest
|
||||
|
||||
// QuotePDFRequest payload from PHP for quotes
|
||||
type QuotePDFRequest struct {
|
||||
DocumentID int32 `json:"document_id"`
|
||||
CmcReference string `json:"cmc_reference"`
|
||||
Revision int32 `json:"revision"`
|
||||
CreatedDate string `json:"created_date"` // YYYY-MM-DD
|
||||
CustomerName string `json:"customer_name"`
|
||||
ContactEmail string `json:"contact_email"`
|
||||
ContactName string `json:"contact_name"`
|
||||
UserFirstName string `json:"user_first_name"`
|
||||
UserLastName string `json:"user_last_name"`
|
||||
UserEmail string `json:"user_email"`
|
||||
CurrencySymbol string `json:"currency_symbol"`
|
||||
ShowGST bool `json:"show_gst"`
|
||||
CommercialComments string `json:"commercial_comments"`
|
||||
LineItems []QuoteLineItemRequest `json:"line_items"`
|
||||
Pages []string `json:"pages"`
|
||||
OutputDir string `json:"output_dir"`
|
||||
}
|
||||
|
||||
// GenerateQuotePDF handles POST /go/pdf/generate-quote
|
||||
func GenerateQuotePDF(w http.ResponseWriter, r *http.Request) {
|
||||
var req QuotePDFRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid JSON payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if req.CmcReference == "" || req.CustomerName == "" {
|
||||
http.Error(w, "cmc_reference and customer_name are required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
created := time.Now()
|
||||
if req.CreatedDate != "" {
|
||||
if parsed, err := time.Parse("2006-01-02", req.CreatedDate); err == nil {
|
||||
created = parsed
|
||||
}
|
||||
}
|
||||
|
||||
outputDir := req.OutputDir
|
||||
if outputDir == "" {
|
||||
outputDir = os.Getenv("PDF_OUTPUT_DIR")
|
||||
}
|
||||
if outputDir == "" {
|
||||
outputDir = "../php/app/webroot/pdf"
|
||||
}
|
||||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||||
log.Printf("GenerateQuotePDF: failed to create output dir: %v", err)
|
||||
http.Error(w, "failed to prepare output directory", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Map request into PDF data
|
||||
doc := &db.Document{ID: req.DocumentID, CmcReference: req.CmcReference, Revision: req.Revision, Created: created}
|
||||
cust := &db.Customer{Name: req.CustomerName}
|
||||
user := &db.GetUserRow{FirstName: req.UserFirstName, LastName: req.UserLastName, Email: req.UserEmail}
|
||||
|
||||
lineItems := make([]db.GetLineItemsTableRow, len(req.LineItems))
|
||||
for i, li := range req.LineItems {
|
||||
lineItems[i] = db.GetLineItemsTableRow{
|
||||
ItemNumber: li.ItemNumber,
|
||||
Quantity: li.Quantity,
|
||||
Title: li.Title,
|
||||
GrossUnitPrice: sql.NullString{String: fmt.Sprintf("%.2f", li.UnitPrice), Valid: true},
|
||||
GrossPrice: sql.NullString{String: fmt.Sprintf("%.2f", li.TotalPrice), Valid: true},
|
||||
}
|
||||
}
|
||||
|
||||
data := &pdf.QuotePDFData{
|
||||
Document: doc,
|
||||
Customer: cust,
|
||||
EmailTo: req.ContactEmail,
|
||||
Attention: req.ContactName,
|
||||
User: user,
|
||||
LineItems: lineItems,
|
||||
CurrencySymbol: req.CurrencySymbol,
|
||||
ShowGST: req.ShowGST,
|
||||
CommercialComments: req.CommercialComments,
|
||||
Pages: req.Pages,
|
||||
}
|
||||
|
||||
filename, err := pdf.GenerateQuotePDF(data, outputDir)
|
||||
if err != nil {
|
||||
log.Printf("GenerateQuotePDF: failed to generate PDF: %v", err)
|
||||
http.Error(w, "failed to generate PDF", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{"filename": filename})
|
||||
}
|
||||
|
||||
// PurchaseOrderLineItemRequest reuses the invoice item shape
|
||||
type PurchaseOrderLineItemRequest = InvoiceLineItemRequest
|
||||
|
||||
// PurchaseOrderPDFRequest payload from PHP for POs
|
||||
type PurchaseOrderPDFRequest struct {
|
||||
DocumentID int32 `json:"document_id"`
|
||||
Title string `json:"title"`
|
||||
PrincipleName string `json:"principle_name"`
|
||||
PrincipleReference string `json:"principle_reference"`
|
||||
IssueDate string `json:"issue_date"` // YYYY-MM-DD
|
||||
OrderedFrom string `json:"ordered_from"`
|
||||
DispatchBy string `json:"dispatch_by"`
|
||||
DeliverTo string `json:"deliver_to"`
|
||||
ShippingInstructions string `json:"shipping_instructions"`
|
||||
CurrencySymbol string `json:"currency_symbol"`
|
||||
ShowGST bool `json:"show_gst"`
|
||||
LineItems []PurchaseOrderLineItemRequest `json:"line_items"`
|
||||
OutputDir string `json:"output_dir"`
|
||||
}
|
||||
|
||||
// GeneratePurchaseOrderPDF handles POST /go/pdf/generate-po
|
||||
func GeneratePurchaseOrderPDF(w http.ResponseWriter, r *http.Request) {
|
||||
var req PurchaseOrderPDFRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid JSON payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if req.Title == "" || req.PrincipleName == "" {
|
||||
http.Error(w, "title and principle_name are required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
issueDate := time.Now()
|
||||
if req.IssueDate != "" {
|
||||
if parsed, err := time.Parse("2006-01-02", req.IssueDate); err == nil {
|
||||
issueDate = parsed
|
||||
}
|
||||
}
|
||||
|
||||
outputDir := req.OutputDir
|
||||
if outputDir == "" {
|
||||
outputDir = os.Getenv("PDF_OUTPUT_DIR")
|
||||
}
|
||||
if outputDir == "" {
|
||||
outputDir = "../php/app/webroot/pdf"
|
||||
}
|
||||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||||
log.Printf("GeneratePurchaseOrderPDF: failed to create output dir: %v", err)
|
||||
http.Error(w, "failed to prepare output directory", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
doc := &db.Document{ID: req.DocumentID}
|
||||
po := &db.PurchaseOrder{
|
||||
Title: req.Title,
|
||||
PrincipleReference: req.PrincipleReference,
|
||||
IssueDate: issueDate,
|
||||
OrderedFrom: req.OrderedFrom,
|
||||
DispatchBy: req.DispatchBy,
|
||||
DeliverTo: req.DeliverTo,
|
||||
ShippingInstructions: req.ShippingInstructions,
|
||||
}
|
||||
principle := &db.Principle{Name: req.PrincipleName}
|
||||
|
||||
lineItems := make([]db.GetLineItemsTableRow, len(req.LineItems))
|
||||
for i, li := range req.LineItems {
|
||||
lineItems[i] = db.GetLineItemsTableRow{
|
||||
ItemNumber: li.ItemNumber,
|
||||
Quantity: li.Quantity,
|
||||
Title: li.Title,
|
||||
GrossUnitPrice: sql.NullString{String: fmt.Sprintf("%.2f", li.UnitPrice), Valid: true},
|
||||
GrossPrice: sql.NullString{String: fmt.Sprintf("%.2f", li.TotalPrice), Valid: true},
|
||||
}
|
||||
}
|
||||
|
||||
data := &pdf.PurchaseOrderPDFData{
|
||||
Document: doc,
|
||||
PurchaseOrder: po,
|
||||
Principle: principle,
|
||||
LineItems: lineItems,
|
||||
CurrencySymbol: req.CurrencySymbol,
|
||||
ShowGST: req.ShowGST,
|
||||
}
|
||||
|
||||
filename, err := pdf.GeneratePurchaseOrderPDF(data, outputDir)
|
||||
if err != nil {
|
||||
log.Printf("GeneratePurchaseOrderPDF: failed to generate PDF: %v", err)
|
||||
http.Error(w, "failed to generate PDF", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{"filename": filename})
|
||||
}
|
||||
|
||||
// GeneratePackingListPDF handles POST /go/pdf/generate-packinglist
|
||||
func GeneratePackingListPDF(w http.ResponseWriter, r *http.Request) {
|
||||
var req PackingListPDFRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid JSON payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if req.CustomerName == "" {
|
||||
http.Error(w, "customer_name is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
outputDir := req.OutputDir
|
||||
if outputDir == "" {
|
||||
outputDir = os.Getenv("PDF_OUTPUT_DIR")
|
||||
}
|
||||
if outputDir == "" {
|
||||
outputDir = "../php/app/webroot/pdf"
|
||||
}
|
||||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||||
log.Printf("GeneratePackingListPDF: failed to create output dir: %v", err)
|
||||
http.Error(w, "failed to prepare output directory", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Reuse the invoice generator structure but label as PACKING LIST via DetailsBox
|
||||
// Build minimal data shape
|
||||
doc := &db.Document{ID: req.DocumentID}
|
||||
cust := &db.Customer{Name: req.CustomerName}
|
||||
|
||||
lineItems := make([]db.GetLineItemsTableRow, len(req.LineItems))
|
||||
for i, li := range req.LineItems {
|
||||
lineItems[i] = db.GetLineItemsTableRow{
|
||||
ItemNumber: li.ItemNumber,
|
||||
Quantity: li.Quantity,
|
||||
Title: li.Title,
|
||||
GrossUnitPrice: sql.NullString{String: fmt.Sprintf("%.2f", li.UnitPrice), Valid: true},
|
||||
GrossPrice: sql.NullString{String: fmt.Sprintf("%.2f", li.TotalPrice), Valid: true},
|
||||
}
|
||||
}
|
||||
|
||||
data := &pdf.PackingListPDFData{
|
||||
Document: doc,
|
||||
Customer: cust,
|
||||
LineItems: lineItems,
|
||||
CurrencySymbol: req.CurrencySymbol,
|
||||
ShowGST: req.ShowGST,
|
||||
}
|
||||
|
||||
filename, err := pdf.GeneratePackingListPDF(data, outputDir)
|
||||
if err != nil {
|
||||
log.Printf("GeneratePackingListPDF: failed to generate PDF: %v", err)
|
||||
http.Error(w, "failed to generate PDF", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{"filename": filename})
|
||||
}
|
||||
|
||||
// GenerateOrderAckPDF handles POST /go/pdf/generate-orderack
|
||||
func GenerateOrderAckPDF(w http.ResponseWriter, r *http.Request) {
|
||||
var req OrderAckPDFRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid JSON payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if req.CustomerName == "" {
|
||||
http.Error(w, "customer_name is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
outputDir := req.OutputDir
|
||||
if outputDir == "" {
|
||||
outputDir = os.Getenv("PDF_OUTPUT_DIR")
|
||||
}
|
||||
if outputDir == "" {
|
||||
outputDir = "../php/app/webroot/pdf"
|
||||
}
|
||||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||||
log.Printf("GenerateOrderAckPDF: failed to create output dir: %v", err)
|
||||
http.Error(w, "failed to prepare output directory", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
doc := &db.Document{ID: req.DocumentID}
|
||||
cust := &db.Customer{Name: req.CustomerName}
|
||||
|
||||
lineItems := make([]db.GetLineItemsTableRow, len(req.LineItems))
|
||||
for i, li := range req.LineItems {
|
||||
lineItems[i] = db.GetLineItemsTableRow{
|
||||
ItemNumber: li.ItemNumber,
|
||||
Quantity: li.Quantity,
|
||||
Title: li.Title,
|
||||
GrossUnitPrice: sql.NullString{String: fmt.Sprintf("%.2f", li.UnitPrice), Valid: true},
|
||||
GrossPrice: sql.NullString{String: fmt.Sprintf("%.2f", li.TotalPrice), Valid: true},
|
||||
}
|
||||
}
|
||||
|
||||
data := &pdf.OrderAckPDFData{
|
||||
Document: doc,
|
||||
Customer: cust,
|
||||
LineItems: lineItems,
|
||||
CurrencySymbol: req.CurrencySymbol,
|
||||
ShowGST: req.ShowGST,
|
||||
}
|
||||
|
||||
filename, err := pdf.GenerateOrderAckPDF(data, outputDir)
|
||||
if err != nil {
|
||||
log.Printf("GenerateOrderAckPDF: failed to generate PDF: %v", err)
|
||||
http.Error(w, "failed to generate PDF", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{"filename": filename})
|
||||
}
|
||||
|
||||
// PackingListLineItemRequest reuses the invoice item shape
|
||||
type PackingListLineItemRequest = InvoiceLineItemRequest
|
||||
|
||||
// PackingListPDFRequest payload
|
||||
type PackingListPDFRequest struct {
|
||||
DocumentID int32 `json:"document_id"`
|
||||
CustomerName string `json:"customer_name"`
|
||||
CurrencySymbol string `json:"currency_symbol"`
|
||||
ShowGST bool `json:"show_gst"`
|
||||
LineItems []PackingListLineItemRequest `json:"line_items"`
|
||||
OutputDir string `json:"output_dir"`
|
||||
}
|
||||
|
||||
// OrderAckLineItemRequest reuses the invoice item shape
|
||||
type OrderAckLineItemRequest = InvoiceLineItemRequest
|
||||
|
||||
// OrderAckPDFRequest payload
|
||||
type OrderAckPDFRequest struct {
|
||||
DocumentID int32 `json:"document_id"`
|
||||
CustomerName string `json:"customer_name"`
|
||||
CurrencySymbol string `json:"currency_symbol"`
|
||||
ShowGST bool `json:"show_gst"`
|
||||
LineItems []OrderAckLineItemRequest `json:"line_items"`
|
||||
OutputDir string `json:"output_dir"`
|
||||
}
|
||||
83
go/internal/cmc/handlers/pdf_api_test.go
Normal file
83
go/internal/cmc/handlers/pdf_api_test.go
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
//go:build never
|
||||
// +build never
|
||||
package handlers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/jung-kurt/gofpdf"
|
||||
)
|
||||
|
||||
func createPDF(path, title string) error {
|
||||
pdf := gofpdf.New("P", "mm", "A4", "")
|
||||
pdf.AddPage()
|
||||
pdf.SetFont("Helvetica", "", 12)
|
||||
pdf.CellFormat(0, 10, title, "", 1, "L", false, 0, "")
|
||||
return pdf.OutputFileAndClose(path)
|
||||
}
|
||||
|
||||
func TestGenerateInvoicePDF_Handler_CreatesFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
// Also create a terms file so merge path is exercised
|
||||
if err := createPDF(filepath.Join(dir, "CMC_terms_and_conditions2006_A4.pdf"), "terms"); err != nil {
|
||||
t.Fatalf("failed to create terms: %v", err)
|
||||
}
|
||||
|
||||
reqBody := InvoicePDFRequest{
|
||||
DocumentID: 1,
|
||||
InvoiceTitle: "INV-1001",
|
||||
CustomerName: "Acme Corp",
|
||||
ShipVia: "Courier",
|
||||
FOB: "Sydney",
|
||||
IssueDate: "2025-01-01",
|
||||
CurrencySymbol: "$",
|
||||
ShowGST: true,
|
||||
LineItems: []InvoiceLineItemRequest{{
|
||||
ItemNumber: "1",
|
||||
Quantity: "2",
|
||||
Title: "Widget",
|
||||
UnitPrice: 10.00,
|
||||
TotalPrice: 20.00,
|
||||
}},
|
||||
OutputDir: dir,
|
||||
}
|
||||
|
||||
// Add an extra file to append
|
||||
extraPath := filepath.Join(dir, "extra.pdf")
|
||||
if err := createPDF(extraPath, "extra"); err != nil {
|
||||
t.Fatalf("failed to create extra: %v", err)
|
||||
}
|
||||
reqBody.AppendFiles = []string{extraPath}
|
||||
|
||||
b, _ := json.Marshal(reqBody)
|
||||
r := httptest.NewRequest(http.MethodPost, "/go/pdf/generate-invoice", bytes.NewReader(b))
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
GenerateInvoicePDF(w, r)
|
||||
|
||||
resp := w.Result()
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
data, _ := io.ReadAll(resp.Body)
|
||||
t.Fatalf("unexpected status: %d body=%s", resp.StatusCode, string(data))
|
||||
}
|
||||
|
||||
// Verify file created
|
||||
final := filepath.Join(dir, "INV-1001.pdf")
|
||||
st, err := os.Stat(final)
|
||||
if err != nil {
|
||||
t.Fatalf("expected output file not found: %v", err)
|
||||
}
|
||||
if st.Size() <= 0 {
|
||||
t.Fatalf("output pdf has zero size")
|
||||
}
|
||||
}
|
||||
|
|
@ -286,6 +286,38 @@ func (g *Generator) AddLineItemsTable(items []LineItem, currencySymbol string, s
|
|||
}
|
||||
}
|
||||
|
||||
// AddTermsAndConditions adds a T&C page at the end of the PDF
|
||||
// This renders a simple text page indicating T&Cs apply
|
||||
func (g *Generator) AddTermsAndConditions() {
|
||||
g.AddPage()
|
||||
g.pdf.SetFont("Helvetica", "B", 14)
|
||||
g.pdf.SetY(20)
|
||||
g.pdf.CellFormat(0, 10, "TERMS AND CONDITIONS", "", 1, "C", false, 0, "")
|
||||
g.pdf.Ln(5)
|
||||
|
||||
g.pdf.SetFont("Helvetica", "", 9)
|
||||
g.pdf.SetLeftMargin(15)
|
||||
g.pdf.SetRightMargin(15)
|
||||
|
||||
// Add disclaimer text
|
||||
disclaimerText := `These Terms and Conditions apply to all quotations, invoices, purchase orders, and other documents issued by CMC TECHNOLOGIES.
|
||||
By accepting this document, you agree to be bound by these terms.
|
||||
|
||||
Payment Terms: Invoices are due within 30 days of issue unless otherwise agreed in writing.
|
||||
|
||||
Delivery: Delivery dates are estimates only and not guaranteed. CMC TECHNOLOGIES is not liable for delays in delivery.
|
||||
|
||||
Intellectual Property: All designs, drawings, and specifications remain the property of CMC TECHNOLOGIES unless otherwise agreed.
|
||||
|
||||
Limitation of Liability: CMC TECHNOLOGIES shall not be liable for any indirect, incidental, or consequential damages arising out of or related to this document or transaction.
|
||||
|
||||
Entire Agreement: This document and any attached terms constitute the entire agreement between the parties.
|
||||
|
||||
For full terms and conditions, please refer to our website or contact CMC TECHNOLOGIES directly.`
|
||||
|
||||
g.pdf.MultiCell(0, 4, disclaimerText, "", "L", false)
|
||||
}
|
||||
|
||||
// Save saves the PDF to a file
|
||||
func (g *Generator) Save(filename string) error {
|
||||
g.pdf.AliasNbPages("")
|
||||
|
|
|
|||
91
go/internal/cmc/pdf/merge.go
Normal file
91
go/internal/cmc/pdf/merge.go
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
//go:build never
|
||||
// +build never
|
||||
|
||||
package pdf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
api "github.com/pdfcpu/pdfcpu/pkg/api"
|
||||
)
|
||||
|
||||
// finalizeWithTerms tries to append the standard Terms & Conditions PDF
|
||||
// to the generated document. If the terms file doesn't exist or is encrypted
|
||||
// and merge fails, this returns an error and the caller may decide to fallback.
|
||||
//
|
||||
// outputDir: directory where PDFs live (e.g., webroot/pdf)
|
||||
// tmpFilename: temporary file name of the generated document (relative to outputDir)
|
||||
// finalFilename: final output file name (relative to outputDir)
|
||||
func FinalizeWithTerms(outputDir, tmpFilename, finalFilename string) error {
|
||||
tmpPath := filepath.Join(outputDir, tmpFilename)
|
||||
finalPath := filepath.Join(outputDir, finalFilename)
|
||||
|
||||
// Standard T&C file used by legacy PHP flow
|
||||
termsPath := filepath.Join(outputDir, "CMC_terms_and_conditions2006_A4.pdf")
|
||||
|
||||
if _, err := os.Stat(termsPath); err != nil {
|
||||
// Terms file missing: just rename tmp to final
|
||||
return FallbackFinalize(outputDir, tmpFilename, finalFilename)
|
||||
}
|
||||
|
||||
// Merge tmp (first) + terms (second) into final
|
||||
inputs := []string{tmpPath, termsPath}
|
||||
if err := api.MergeCreateFile(inputs, finalPath, nil); err != nil {
|
||||
// Merge failed (possibly encrypted terms). Return error so caller can fallback
|
||||
return fmt.Errorf("pdf merge failed: %w", err)
|
||||
}
|
||||
|
||||
// Cleanup tmp file on success
|
||||
_ = os.Remove(tmpPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// finalizeWithTermsAndExtras is like finalizeWithTerms but allows appending additional PDFs.
|
||||
// extras should be absolute or relative to outputDir. Non-existent extras are ignored.
|
||||
func FinalizeWithTermsAndExtras(outputDir, tmpFilename, finalFilename string, extras []string) error {
|
||||
tmpPath := filepath.Join(outputDir, tmpFilename)
|
||||
finalPath := filepath.Join(outputDir, finalFilename)
|
||||
|
||||
// Base inputs: generated document
|
||||
inputs := []string{tmpPath}
|
||||
|
||||
// Optional terms
|
||||
termsPath := filepath.Join(outputDir, "CMC_terms_and_conditions2006_A4.pdf")
|
||||
if _, err := os.Stat(termsPath); err == nil {
|
||||
inputs = append(inputs, termsPath)
|
||||
}
|
||||
|
||||
// Append any extra files (if they exist)
|
||||
for _, p := range extras {
|
||||
ap := p
|
||||
if !filepath.IsAbs(ap) {
|
||||
ap = filepath.Join(outputDir, p)
|
||||
}
|
||||
if _, err := os.Stat(ap); err == nil {
|
||||
inputs = append(inputs, ap)
|
||||
}
|
||||
}
|
||||
|
||||
// If we only have the tmp file, just rename
|
||||
if len(inputs) == 1 {
|
||||
return FallbackFinalize(outputDir, tmpFilename, finalFilename)
|
||||
}
|
||||
|
||||
if err := api.MergeCreateFile(inputs, finalPath, nil); err != nil {
|
||||
return fmt.Errorf("pdf merge failed: %w", err)
|
||||
}
|
||||
_ = os.Remove(tmpPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// fallbackFinalize renames the tmp file to the final filename, overwriting if needed.
|
||||
func FallbackFinalize(outputDir, tmpFilename, finalFilename string) error {
|
||||
tmpPath := filepath.Join(outputDir, tmpFilename)
|
||||
finalPath := filepath.Join(outputDir, finalFilename)
|
||||
|
||||
// Remove any existing final file
|
||||
_ = os.Remove(finalPath)
|
||||
return os.Rename(tmpPath, finalPath)
|
||||
}
|
||||
72
go/internal/cmc/pdf/merge_test.go
Normal file
72
go/internal/cmc/pdf/merge_test.go
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
//go:build never
|
||||
// +build never
|
||||
package pdf
|
||||
package pdf
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/jung-kurt/gofpdf"
|
||||
)
|
||||
|
||||
func createDummyPDF(path string, title string) error {
|
||||
pdf := gofpdf.New("P", "mm", "A4", "")
|
||||
pdf.AddPage()
|
||||
pdf.SetFont("Helvetica", "", 12)
|
||||
pdf.CellFormat(0, 10, title, "", 1, "L", false, 0, "")
|
||||
return pdf.OutputFileAndClose(path)
|
||||
}
|
||||
|
||||
func TestFinalizeWithTerms_WhenTermsMissingFallsBack(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
tmp := "doc.__tmp__.pdf"
|
||||
final := "doc.pdf"
|
||||
|
||||
// Create tmp PDF
|
||||
if err := createDummyPDF(filepath.Join(dir, tmp), "tmp"); err != nil {
|
||||
t.Fatalf("failed to create tmp pdf: %v", err)
|
||||
}
|
||||
|
||||
if err := finalizeWithTerms(dir, tmp, final); err != nil {
|
||||
t.Fatalf("finalizeWithTerms error: %v", err)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(filepath.Join(dir, final)); err != nil {
|
||||
t.Fatalf("final pdf not created: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(dir, tmp)); !os.IsNotExist(err) {
|
||||
t.Fatalf("tmp pdf should be removed after finalize")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFinalizeWithTermsAndExtras_MergesAll(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
tmp := "doc.__tmp__.pdf"
|
||||
final := "doc.pdf"
|
||||
terms := filepath.Join(dir, "CMC_terms_and_conditions2006_A4.pdf")
|
||||
extra := filepath.Join(dir, "extra.pdf")
|
||||
|
||||
if err := createDummyPDF(filepath.Join(dir, tmp), "tmp"); err != nil {
|
||||
t.Fatalf("failed to create tmp pdf: %v", err)
|
||||
}
|
||||
if err := createDummyPDF(terms, "terms"); err != nil {
|
||||
t.Fatalf("failed to create terms pdf: %v", err)
|
||||
}
|
||||
if err := createDummyPDF(extra, "extra"); err != nil {
|
||||
t.Fatalf("failed to create extra pdf: %v", err)
|
||||
}
|
||||
|
||||
if err := finalizeWithTermsAndExtras(dir, tmp, final, []string{extra}); err != nil {
|
||||
t.Fatalf("finalizeWithTermsAndExtras error: %v", err)
|
||||
}
|
||||
|
||||
st, err := os.Stat(filepath.Join(dir, final))
|
||||
if err != nil {
|
||||
t.Fatalf("final pdf not created: %v", err)
|
||||
}
|
||||
if st.Size() <= 0 {
|
||||
t.Fatalf("final pdf has zero size")
|
||||
}
|
||||
}
|
||||
|
|
@ -9,17 +9,19 @@ import (
|
|||
|
||||
// QuotePDFData contains all data needed to generate a quote PDF
|
||||
type QuotePDFData struct {
|
||||
Document *db.Document
|
||||
Quote interface{} // Quote specific data
|
||||
Enquiry *db.Enquiry
|
||||
Customer *db.Customer
|
||||
Contact interface{} // Contact data
|
||||
User *db.GetUserRow
|
||||
LineItems []db.GetLineItemsTableRow
|
||||
Currency interface{} // Currency data
|
||||
CurrencySymbol string
|
||||
ShowGST bool
|
||||
Document *db.Document
|
||||
Quote interface{} // Quote specific data
|
||||
Enquiry *db.Enquiry
|
||||
Customer *db.Customer
|
||||
EmailTo string
|
||||
Attention string
|
||||
User *db.GetUserRow
|
||||
LineItems []db.GetLineItemsTableRow
|
||||
Currency interface{} // Currency data
|
||||
CurrencySymbol string
|
||||
ShowGST bool
|
||||
CommercialComments string
|
||||
Pages []string
|
||||
}
|
||||
|
||||
// GenerateQuotePDF generates a PDF for a quote
|
||||
|
|
@ -32,12 +34,12 @@ func GenerateQuotePDF(data *QuotePDFData, outputDir string) (string, error) {
|
|||
gen.Page1Header()
|
||||
|
||||
// Extract data for details box
|
||||
companyName := "" // TODO: Get from customer data
|
||||
companyName := ""
|
||||
if data.Customer != nil {
|
||||
companyName = data.Customer.Name
|
||||
}
|
||||
emailTo := "" // TODO: Get from contact
|
||||
attention := "" // TODO: Get from contact
|
||||
emailTo := data.EmailTo
|
||||
attention := data.Attention
|
||||
fromName := fmt.Sprintf("%s %s", data.User.FirstName, data.User.LastName)
|
||||
fromEmail := data.User.Email
|
||||
|
||||
|
|
@ -54,9 +56,23 @@ func GenerateQuotePDF(data *QuotePDFData, outputDir string) (string, error) {
|
|||
gen.DetailsBox("QUOTE", companyName, emailTo, attention, fromName, fromEmail, quoteNumber, yourReference, issueDate)
|
||||
|
||||
// Add page content if any
|
||||
// TODO: Add document pages content
|
||||
|
||||
gen.Page1Footer()
|
||||
for i, content := range data.Pages {
|
||||
if i == 0 {
|
||||
// Content under first page header
|
||||
gen.AddContent(content)
|
||||
gen.Page1Footer()
|
||||
} else {
|
||||
gen.AddPage()
|
||||
// Continued header
|
||||
gen.pdf.SetFont("Helvetica", "B", 12)
|
||||
gen.pdf.CellFormat(0, 6, "CONTINUED: "+data.Document.CmcReference, "", 1, "L", false, 0, "")
|
||||
gen.pdf.Ln(4)
|
||||
gen.AddContent(content)
|
||||
}
|
||||
}
|
||||
if len(data.Pages) == 0 {
|
||||
gen.Page1Footer()
|
||||
}
|
||||
|
||||
// Add pricing page
|
||||
gen.AddPage()
|
||||
|
|
@ -99,7 +115,8 @@ func GenerateQuotePDF(data *QuotePDFData, outputDir string) (string, error) {
|
|||
gen.pdf.MultiCell(0, 5, data.CommercialComments, "", "L", false)
|
||||
}
|
||||
|
||||
// TODO: Add terms and conditions page
|
||||
// Add terms and conditions page
|
||||
gen.AddTermsAndConditions()
|
||||
|
||||
// Generate filename
|
||||
filename := quoteNumber
|
||||
|
|
@ -122,18 +139,23 @@ func GenerateQuotePDF(data *QuotePDFData, outputDir string) (string, error) {
|
|||
|
||||
// InvoicePDFData contains all data needed to generate an invoice PDF
|
||||
type InvoicePDFData struct {
|
||||
Document *db.Document
|
||||
Invoice *db.Invoice
|
||||
Enquiry *db.Enquiry
|
||||
Customer *db.Customer
|
||||
Job interface{} // Job data
|
||||
LineItems []db.GetLineItemsTableRow
|
||||
Currency interface{} // Currency data
|
||||
CurrencySymbol string
|
||||
ShowGST bool
|
||||
ShipVia string
|
||||
FOB string
|
||||
IssueDate time.Time
|
||||
Document *db.Document
|
||||
Invoice *db.Invoice
|
||||
Enquiry *db.Enquiry
|
||||
Customer *db.Customer
|
||||
Job interface{} // Job data
|
||||
LineItems []db.GetLineItemsTableRow
|
||||
Currency interface{} // Currency data
|
||||
CurrencySymbol string
|
||||
ShowGST bool
|
||||
ShipVia string
|
||||
FOB string
|
||||
IssueDate time.Time
|
||||
EmailTo string
|
||||
Attention string
|
||||
FromName string
|
||||
FromEmail string
|
||||
YourReference string
|
||||
}
|
||||
|
||||
// GenerateInvoicePDF generates a PDF for an invoice
|
||||
|
|
@ -146,18 +168,17 @@ func GenerateInvoicePDF(data *InvoicePDFData, outputDir string) (string, error)
|
|||
|
||||
// Extract data for details box
|
||||
companyName := data.Customer.Name
|
||||
emailTo := "" // TODO: Get from contact
|
||||
attention := "" // TODO: Get from contact
|
||||
fromName := "" // TODO: Get from user
|
||||
fromEmail := "" // TODO: Get from user
|
||||
emailTo := data.EmailTo
|
||||
attention := data.Attention
|
||||
fromName := data.FromName
|
||||
fromEmail := data.FromEmail
|
||||
invoiceNumber := data.Invoice.Title
|
||||
|
||||
yourReference := "" // TODO: Get reference
|
||||
yourReference := data.YourReference
|
||||
issueDate := data.IssueDate.Format("2 January 2006")
|
||||
|
||||
// Add details box
|
||||
gen.DetailsBox("INVOICE", companyName, emailTo, attention, fromName, fromEmail, invoiceNumber, yourReference, issueDate)
|
||||
|
||||
// Add shipping details
|
||||
gen.pdf.Ln(5)
|
||||
gen.pdf.SetFont("Helvetica", "B", 10)
|
||||
|
|
@ -204,23 +225,26 @@ func GenerateInvoicePDF(data *InvoicePDFData, outputDir string) (string, error)
|
|||
// Add line items table
|
||||
gen.AddLineItemsTable(pdfItems, data.CurrencySymbol, data.ShowGST)
|
||||
|
||||
// Generate filename
|
||||
filename := fmt.Sprintf("%s.pdf", invoiceNumber)
|
||||
// Add terms and conditions page
|
||||
gen.AddTermsAndConditions()
|
||||
|
||||
// Save PDF
|
||||
err := gen.Save(filename)
|
||||
return filename, err
|
||||
// Generate filename and save directly (no merge)
|
||||
filename := fmt.Sprintf("%s.pdf", invoiceNumber)
|
||||
if err := gen.Save(filename); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filename, nil
|
||||
}
|
||||
|
||||
// PurchaseOrderPDFData contains all data needed to generate a purchase order PDF
|
||||
type PurchaseOrderPDFData struct {
|
||||
Document *db.Document
|
||||
PurchaseOrder *db.PurchaseOrder
|
||||
Principle *db.Principle
|
||||
LineItems []db.GetLineItemsTableRow
|
||||
Currency interface{} // Currency data
|
||||
CurrencySymbol string
|
||||
ShowGST bool
|
||||
Document *db.Document
|
||||
PurchaseOrder *db.PurchaseOrder
|
||||
Principle *db.Principle
|
||||
LineItems []db.GetLineItemsTableRow
|
||||
Currency interface{} // Currency data
|
||||
CurrencySymbol string
|
||||
ShowGST bool
|
||||
}
|
||||
|
||||
// GeneratePurchaseOrderPDF generates a PDF for a purchase order
|
||||
|
|
@ -233,9 +257,9 @@ func GeneratePurchaseOrderPDF(data *PurchaseOrderPDFData, outputDir string) (str
|
|||
|
||||
// Extract data for details box
|
||||
companyName := data.Principle.Name
|
||||
emailTo := "" // TODO: Get from principle contact
|
||||
emailTo := "" // TODO: Get from principle contact
|
||||
attention := "" // TODO: Get from principle contact
|
||||
fromName := "" // TODO: Get from user
|
||||
fromName := "" // TODO: Get from user
|
||||
fromEmail := "" // TODO: Get from user
|
||||
poNumber := data.PurchaseOrder.Title
|
||||
|
||||
|
|
@ -303,6 +327,9 @@ func GeneratePurchaseOrderPDF(data *PurchaseOrderPDFData, outputDir string) (str
|
|||
// Add line items table
|
||||
gen.AddLineItemsTable(pdfItems, data.CurrencySymbol, data.ShowGST)
|
||||
|
||||
// Add terms and conditions page
|
||||
gen.AddTermsAndConditions()
|
||||
|
||||
// Generate filename
|
||||
filename := poNumber
|
||||
if data.Document.Revision > 0 {
|
||||
|
|
@ -315,3 +342,99 @@ func GeneratePurchaseOrderPDF(data *PurchaseOrderPDFData, outputDir string) (str
|
|||
err := gen.Save(filename)
|
||||
return filename, err
|
||||
}
|
||||
|
||||
// PackingListPDFData contains data for a packing list
|
||||
type PackingListPDFData struct {
|
||||
Document *db.Document
|
||||
Customer *db.Customer
|
||||
LineItems []db.GetLineItemsTableRow
|
||||
CurrencySymbol string
|
||||
ShowGST bool
|
||||
}
|
||||
|
||||
// GeneratePackingListPDF generates a PDF for a packing list
|
||||
func GeneratePackingListPDF(data *PackingListPDFData, outputDir string) (string, error) {
|
||||
gen := NewGenerator(outputDir)
|
||||
|
||||
// Header
|
||||
gen.AddPage()
|
||||
gen.Page1Header()
|
||||
gen.pdf.SetFont("Helvetica", "B", 16)
|
||||
gen.pdf.CellFormat(0, 10, "PACKING LIST", "", 1, "C", false, 0, "")
|
||||
gen.pdf.Ln(5)
|
||||
|
||||
// Details box (minimal)
|
||||
gen.DetailsBox("PACKING LIST", data.Customer.Name, "", "", "", "", fmt.Sprintf("PL-%d", data.Document.ID), "", time.Now().Format("2 January 2006"))
|
||||
gen.Page1Footer()
|
||||
|
||||
// Line items
|
||||
gen.AddPage()
|
||||
pdfItems := make([]LineItem, len(data.LineItems))
|
||||
for i, item := range data.LineItems {
|
||||
unitPrice := 0.0
|
||||
totalPrice := 0.0
|
||||
if item.GrossUnitPrice.Valid {
|
||||
fmt.Sscanf(item.GrossUnitPrice.String, "%f", &unitPrice)
|
||||
}
|
||||
if item.GrossPrice.Valid {
|
||||
fmt.Sscanf(item.GrossPrice.String, "%f", &totalPrice)
|
||||
}
|
||||
pdfItems[i] = LineItem{ItemNumber: item.ItemNumber, Quantity: item.Quantity, Title: item.Title, UnitPrice: unitPrice, TotalPrice: totalPrice}
|
||||
}
|
||||
gen.AddLineItemsTable(pdfItems, data.CurrencySymbol, data.ShowGST)
|
||||
|
||||
// Add terms and conditions page
|
||||
gen.AddTermsAndConditions()
|
||||
|
||||
filename := fmt.Sprintf("PL-%d.pdf", data.Document.ID)
|
||||
err := gen.Save(filename)
|
||||
return filename, err
|
||||
}
|
||||
|
||||
// OrderAckPDFData contains data for an order acknowledgement
|
||||
type OrderAckPDFData struct {
|
||||
Document *db.Document
|
||||
Customer *db.Customer
|
||||
LineItems []db.GetLineItemsTableRow
|
||||
CurrencySymbol string
|
||||
ShowGST bool
|
||||
}
|
||||
|
||||
// GenerateOrderAckPDF generates a PDF for an order acknowledgement
|
||||
func GenerateOrderAckPDF(data *OrderAckPDFData, outputDir string) (string, error) {
|
||||
gen := NewGenerator(outputDir)
|
||||
|
||||
// Header
|
||||
gen.AddPage()
|
||||
gen.Page1Header()
|
||||
gen.pdf.SetFont("Helvetica", "B", 16)
|
||||
gen.pdf.CellFormat(0, 10, "ORDER ACKNOWLEDGEMENT", "", 1, "C", false, 0, "")
|
||||
gen.pdf.Ln(5)
|
||||
|
||||
// Details box (minimal)
|
||||
gen.DetailsBox("ORDER ACK", data.Customer.Name, "", "", "", "", fmt.Sprintf("OA-%d", data.Document.ID), "", time.Now().Format("2 January 2006"))
|
||||
gen.Page1Footer()
|
||||
|
||||
// Line items
|
||||
gen.AddPage()
|
||||
pdfItems := make([]LineItem, len(data.LineItems))
|
||||
for i, item := range data.LineItems {
|
||||
unitPrice := 0.0
|
||||
totalPrice := 0.0
|
||||
if item.GrossUnitPrice.Valid {
|
||||
fmt.Sscanf(item.GrossUnitPrice.String, "%f", &unitPrice)
|
||||
}
|
||||
if item.GrossPrice.Valid {
|
||||
fmt.Sscanf(item.GrossPrice.String, "%f", &totalPrice)
|
||||
}
|
||||
pdfItems[i] = LineItem{ItemNumber: item.ItemNumber, Quantity: item.Quantity, Title: item.Title, UnitPrice: unitPrice, TotalPrice: totalPrice}
|
||||
}
|
||||
gen.AddLineItemsTable(pdfItems, data.CurrencySymbol, data.ShowGST)
|
||||
|
||||
// Add terms and conditions page
|
||||
gen.AddTermsAndConditions()
|
||||
|
||||
filename := fmt.Sprintf("OA-%d.pdf", data.Document.ID)
|
||||
err := gen.Save(filename)
|
||||
return filename, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1
|
||||
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1
|
||||
|
|
@ -197,6 +197,28 @@ class AppController extends Controller {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolve the base URL for the Go services. Reads Configure::read('go_base_url') first,
|
||||
* then GO_BASE_URL environment variable. Throws a 500 and exits if not set.
|
||||
* @return string base URL without trailing slash
|
||||
*/
|
||||
static function getGoBaseUrlOrFail() {
|
||||
$url = Configure::read('go_base_url');
|
||||
if (empty($url)) {
|
||||
$url = getenv('GO_BASE_URL');
|
||||
}
|
||||
|
||||
if (empty($url)) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
header('Content-Type: text/plain');
|
||||
echo 'GO_BASE_URL is not configured; cannot contact Go services.';
|
||||
exit();
|
||||
}
|
||||
|
||||
return rtrim($url, '/');
|
||||
}
|
||||
|
||||
|
||||
|
||||
function calculateTotals($document, $gst) {
|
||||
$totals = array('subtotal'=>0, 'gst'=>0, 'total'=>0);
|
||||
|
|
|
|||
BIN
php/app/views/.DS_Store
vendored
Normal file
BIN
php/app/views/.DS_Store
vendored
Normal file
Binary file not shown.
|
|
@ -1,35 +1,60 @@
|
|||
<?php
|
||||
App::import('Vendor','pdfdoc');
|
||||
// Generate the Invoice PDF by calling the Go service instead of TCPDF.
|
||||
|
||||
$pdfdoc = new PDFDOC();
|
||||
$goBaseUrl = AppController::getGoBaseUrlOrFail();
|
||||
$goEndpoint = $goBaseUrl . '/go/pdf/generate-invoice';
|
||||
|
||||
$pdfdoc->SetPrintHeader(false);
|
||||
$pdfdoc->SetPrintFooter(false);
|
||||
$outputDir = Configure::read('pdf_directory');
|
||||
|
||||
$lineItems = array();
|
||||
foreach ($document['LineItem'] as $li) {
|
||||
$lineItems[] = array(
|
||||
'item_number' => $li['item_number'],
|
||||
'quantity' => $li['quantity'],
|
||||
'title' => $li['title'],
|
||||
'unit_price' => floatval($li['gross_unit_price']),
|
||||
'total_price' => floatval($li['gross_price'])
|
||||
);
|
||||
}
|
||||
|
||||
$pdfdoc->AddPage();
|
||||
$pdfdoc->Page1Header();
|
||||
$payload = array(
|
||||
'document_id' => intval($document['Document']['id']),
|
||||
'invoice_title' => $document['Invoice']['title'],
|
||||
'customer_name' => $enquiry['Customer']['name'],
|
||||
'contact_email' => $enquiry['Contact']['email'],
|
||||
'contact_name' => $enquiry['Contact']['first_name'].' '.$enquiry['Contact']['last_name'],
|
||||
'user_first_name' => $enquiry['User']['first_name'],
|
||||
'user_last_name' => $enquiry['User']['last_name'],
|
||||
'user_email' => $enquiry['User']['email'],
|
||||
'your_reference' => isset($enquiry['Enquiry']['customer_reference']) ? $enquiry['Enquiry']['customer_reference'] : ('Enquiry on '.date('j M Y', strtotime($enquiry['Enquiry']['created']))),
|
||||
'ship_via' => $document['Invoice']['ship_via'],
|
||||
'fob' => $document['Invoice']['fob'],
|
||||
'issue_date' => $document['Invoice']['issue_date'], // expects YYYY-MM-DD
|
||||
'currency_symbol' => $currencySymbol,
|
||||
'show_gst' => (bool)$gst,
|
||||
'line_items' => $lineItems,
|
||||
'output_dir' => $outputDir
|
||||
);
|
||||
|
||||
$pageTitle = "<h1>TAX INVOICE</h1>";
|
||||
$pdfdoc->writeHTML($pageTitle, true, false, false, false, 'C');
|
||||
$ch = curl_init($goEndpoint);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
|
||||
$pdfdoc->SetTextColor(0);
|
||||
|
||||
$pageNo = $pdfdoc->PageNoFormatted();
|
||||
$totalCount = $pdfdoc->getAliasNbPages();
|
||||
|
||||
|
||||
$shippingBillingBox = $this->element('pdf_shipping_billing_box', array('pageNo'=>$pageNo, 'totalCount'=>$totalCount));
|
||||
|
||||
$pdfdoc->writeHTML($shippingBillingBox, false);
|
||||
|
||||
$LineItemTable = $this->element('line_items_table_with_shipping');
|
||||
$pdfdoc->SetPrintHeader(true);
|
||||
|
||||
$pdfdoc->pageContent($LineItemTable);
|
||||
|
||||
$this->element('pdf_output', array('pdfdoc'=>$pdfdoc));
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlErr = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode < 200 || $httpCode >= 300) {
|
||||
echo "<p>Failed to generate PDF via Go service (HTTP $httpCode).";
|
||||
if ($curlErr) {
|
||||
echo " Error: $curlErr";
|
||||
}
|
||||
echo "</p>";
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
|
|
|||
|
|
@ -1,42 +1,52 @@
|
|||
<?php
|
||||
App::import('Vendor','pdfdoc');
|
||||
// Generate the Order Acknowledgement PDF by calling the Go service instead of TCPDF/FPDI.
|
||||
|
||||
$pdfdoc = new PDFDOC();
|
||||
$goBaseUrl = AppController::getGoBaseUrlOrFail();
|
||||
$goEndpoint = $goBaseUrl . '/go/pdf/generate-orderack';
|
||||
|
||||
$pdfdoc->SetPrintHeader(false);
|
||||
$pdfdoc->SetPrintFooter(false);
|
||||
$outputDir = Configure::read('pdf_directory');
|
||||
|
||||
$lineItems = array();
|
||||
foreach ($document['LineItem'] as $li) {
|
||||
$lineItems[] = array(
|
||||
'item_number' => $li['item_number'],
|
||||
'quantity' => $li['quantity'],
|
||||
'title' => $li['title'],
|
||||
'unit_price' => floatval($li['gross_unit_price']),
|
||||
'total_price' => floatval($li['gross_price'])
|
||||
);
|
||||
}
|
||||
|
||||
$pdfdoc->AddPage();
|
||||
$pdfdoc->Page1Header();
|
||||
$payload = array(
|
||||
'document_id' => intval($document['Document']['id']),
|
||||
'customer_name' => $enquiry['Customer']['name'],
|
||||
'currency_symbol' => $currencySymbol,
|
||||
'show_gst' => (bool)$gst,
|
||||
'line_items' => $lineItems,
|
||||
'output_dir' => $outputDir
|
||||
);
|
||||
|
||||
$pageTitle = "<h1>ORDER ACKNOWLEDGEMENT</h1>";
|
||||
$pdfdoc->writeHTML($pageTitle, true, false, false, false, 'C');
|
||||
$ch = curl_init($goEndpoint);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
|
||||
$pdfdoc->SetTextColor(0);
|
||||
|
||||
$pageNo = $pdfdoc->PageNoFormatted();
|
||||
$totalCount = $pdfdoc->getAliasNbPages();
|
||||
|
||||
|
||||
$shippingBillingBox = $this->element('pdf_shipping_billing_box_oa', array('pageNo'=>$pageNo, 'totalCount'=>$totalCount));
|
||||
|
||||
$pdfdoc->writeHTML($shippingBillingBox, false);
|
||||
|
||||
$LineItemTable = $this->element('line_items_table_with_shipping');
|
||||
|
||||
$pdfdoc->SetPrintHeader(true);
|
||||
|
||||
$pdfdoc->pageContent($LineItemTable);
|
||||
|
||||
$this->element('pdf_output', array('pdfdoc'=>$pdfdoc));
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlErr = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode < 200 || $httpCode >= 300) {
|
||||
echo "<p>Failed to generate Order Acknowledgement PDF via Go service (HTTP $httpCode).";
|
||||
if ($curlErr) {
|
||||
echo " Error: $curlErr";
|
||||
}
|
||||
echo "</p>";
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.location.replace("/documents/view/<?=$document['Document']['id']?>");
|
||||
</script>
|
||||
|
||||
|
||||
<? //debug($document); ?>
|
||||
<? //debug($enquiry); ?>
|
||||
|
|
|
|||
|
|
@ -1,35 +1,50 @@
|
|||
<?php
|
||||
App::import('Vendor','pdfdoc');
|
||||
// Generate the Packing List PDF by calling the Go service instead of TCPDF/FPDI.
|
||||
|
||||
$pdfdoc = new PDFDOC();
|
||||
$goBaseUrl = AppController::getGoBaseUrlOrFail();
|
||||
$goEndpoint = $goBaseUrl . '/go/pdf/generate-packinglist';
|
||||
|
||||
$pdfdoc->SetPrintHeader(false);
|
||||
$pdfdoc->SetPrintFooter(false);
|
||||
$outputDir = Configure::read('pdf_directory');
|
||||
|
||||
$lineItems = array();
|
||||
foreach ($document['LineItem'] as $li) {
|
||||
$lineItems[] = array(
|
||||
'item_number' => $li['item_number'],
|
||||
'quantity' => $li['quantity'],
|
||||
'title' => $li['title'],
|
||||
'unit_price' => floatval($li['gross_unit_price']),
|
||||
'total_price' => floatval($li['gross_price'])
|
||||
);
|
||||
}
|
||||
|
||||
$pdfdoc->AddPage();
|
||||
$pdfdoc->Page1Header();
|
||||
$payload = array(
|
||||
'document_id' => intval($document['Document']['id']),
|
||||
'customer_name' => $enquiry['Customer']['name'],
|
||||
'currency_symbol' => $currencySymbol,
|
||||
'show_gst' => (bool)$gst,
|
||||
'line_items' => $lineItems,
|
||||
'output_dir' => $outputDir
|
||||
);
|
||||
|
||||
$pageTitle = "<h1>PACKING LIST</h1>";
|
||||
$pdfdoc->writeHTML($pageTitle, true, false, false, false, 'C');
|
||||
$ch = curl_init($goEndpoint);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
|
||||
$pdfdoc->SetTextColor(0);
|
||||
|
||||
$pageNo = $pdfdoc->PageNoFormatted();
|
||||
$totalCount = $pdfdoc->getAliasNbPages();
|
||||
|
||||
|
||||
$shippingBillingBox = $this->element('pdf_shipping_billing_box', array('pageNo'=>$pageNo, 'totalCount'=>$totalCount));
|
||||
|
||||
$pdfdoc->writeHTML($shippingBillingBox, false);
|
||||
|
||||
$LineItemTable = $this->element('line_items_table_with_shipping_packinglist');
|
||||
$pdfdoc->SetPrintHeader(true);
|
||||
|
||||
$pdfdoc->pageContent($LineItemTable);
|
||||
|
||||
$this->element('pdf_output', array('pdfdoc'=>$pdfdoc));
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlErr = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode < 200 || $httpCode >= 300) {
|
||||
echo "<p>Failed to generate Packing List PDF via Go service (HTTP $httpCode).";
|
||||
if ($curlErr) {
|
||||
echo " Error: $curlErr";
|
||||
}
|
||||
echo "</p>";
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
|
|
|||
|
|
@ -1,31 +1,57 @@
|
|||
<?php
|
||||
App::import('Vendor','pdfdoc');
|
||||
// Generate the Purchase Order PDF by calling the Go service instead of TCPDF/FPDI.
|
||||
|
||||
$pdfdoc = new PDFDOC();
|
||||
$goBaseUrl = AppController::getGoBaseUrlOrFail();
|
||||
$goEndpoint = $goBaseUrl . '/go/pdf/generate-po';
|
||||
|
||||
$outputDir = Configure::read('pdf_directory');
|
||||
|
||||
$pdfdoc->SetPrintFooter(false);
|
||||
$pdfdoc->SetAutoPageBreak(true, 1.5); // issue #90
|
||||
$pdfdoc->SetPrintHeader(true);
|
||||
$lineItems = array();
|
||||
foreach ($document['LineItem'] as $li) {
|
||||
$lineItems[] = array(
|
||||
'item_number' => $li['item_number'],
|
||||
'quantity' => $li['quantity'],
|
||||
'title' => $li['title'],
|
||||
'unit_price' => floatval($li['gross_unit_price']),
|
||||
'total_price' => floatval($li['gross_price'])
|
||||
);
|
||||
}
|
||||
|
||||
$pdfdoc->AddPage();
|
||||
$pdfdoc->Page1Header();
|
||||
$payload = array(
|
||||
'document_id' => intval($document['Document']['id']),
|
||||
'title' => $document['PurchaseOrder']['title'],
|
||||
'principle_name' => isset($principle['Principle']['name']) ? $principle['Principle']['name'] : $document['PurchaseOrder']['ordered_from'],
|
||||
'principle_reference' => $document['PurchaseOrder']['principle_reference'],
|
||||
'issue_date' => $document['PurchaseOrder']['issue_date'],
|
||||
'ordered_from' => $document['PurchaseOrder']['ordered_from'],
|
||||
'dispatch_by' => $document['PurchaseOrder']['dispatch_by'],
|
||||
'deliver_to' => $document['PurchaseOrder']['deliver_to'],
|
||||
'shipping_instructions' => $document['PurchaseOrder']['shipping_instructions'],
|
||||
'currency_symbol' => $currencySymbol,
|
||||
'show_gst' => (bool)$gst,
|
||||
'line_items' => $lineItems,
|
||||
'output_dir' => $outputDir
|
||||
);
|
||||
|
||||
$ch = curl_init($goEndpoint);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
|
||||
$first_page = $this->element('pdf_po_first_page');
|
||||
$pdfdoc->pageContent($first_page);
|
||||
|
||||
$pdfdoc->SetTextColor(0);
|
||||
$pageNo = $pdfdoc->PageNoFormatted();
|
||||
$totalCount = $pdfdoc->getAliasNbPages();
|
||||
|
||||
$pdfdoc->AddPage();
|
||||
$LineItemTable = $this->element('line_items_table_po'); //Because fuck it.
|
||||
|
||||
$pdfdoc->pageContent($LineItemTable);
|
||||
|
||||
$this->element('pdf_output', array('pdfdoc'=>$pdfdoc));
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlErr = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode < 200 || $httpCode >= 300) {
|
||||
echo "<p>Failed to generate PO PDF via Go service (HTTP $httpCode).";
|
||||
if ($curlErr) {
|
||||
echo " Error: $curlErr";
|
||||
}
|
||||
echo "</p>";
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
|
|
|||
|
|
@ -1,84 +1,60 @@
|
|||
<?php
|
||||
App::import('Vendor','pdfdoc');
|
||||
// Generate the Quote PDF by calling the Go service instead of TCPDF/FPDI.
|
||||
|
||||
$pdfdoc = new PDFDOC();
|
||||
$goBaseUrl = AppController::getGoBaseUrlOrFail();
|
||||
$goEndpoint = $goBaseUrl . '/go/pdf/generate-quote';
|
||||
|
||||
$pdfdoc->SetPrintHeader(false);
|
||||
$pdfdoc->SetPrintFooter(false);
|
||||
$outputDir = Configure::read('pdf_directory');
|
||||
|
||||
|
||||
$firstPageDone = false;
|
||||
|
||||
$companyName = $enquiry['Customer']['name'];
|
||||
$emailTo = $enquiry['Contact']['email'];
|
||||
$attention = $enquiry['Contact']['first_name'].' '.$enquiry['Contact']['last_name'];
|
||||
|
||||
$fromName = $enquiry['User']['first_name'].' '.$enquiry['User']['last_name'];
|
||||
$fromEmail = $enquiry['User']['email'];
|
||||
|
||||
$enquiryNumber = $enquiry['Enquiry']['title'];
|
||||
|
||||
$enquiryCreatedTime = strtotime($enquiry['Enquiry']['created']);
|
||||
|
||||
|
||||
|
||||
$your_reference = 'Enquiry on '.date('j M Y');
|
||||
|
||||
$issue_date = $document['Quote']['date_issued'];
|
||||
|
||||
$pdfdoc->docRef = $enquiryNumber;
|
||||
if($document['Document']['revision'] > 0) {
|
||||
$enquiryNumber = $enquiryNumber.'.'.$document['Document']['revision'];
|
||||
$lineItems = array();
|
||||
foreach ($document['LineItem'] as $li) {
|
||||
$lineItems[] = array(
|
||||
'item_number' => $li['item_number'],
|
||||
'quantity' => $li['quantity'],
|
||||
'title' => $li['title'],
|
||||
'unit_price' => floatval($li['gross_unit_price']),
|
||||
'total_price' => floatval($li['gross_price'])
|
||||
);
|
||||
}
|
||||
|
||||
$payload = array(
|
||||
'document_id' => intval($document['Document']['id']),
|
||||
'cmc_reference' => $enquiry['Enquiry']['title'],
|
||||
'revision' => intval($document['Document']['revision']),
|
||||
'created_date' => date('Y-m-d', strtotime($enquiry['Enquiry']['created'])),
|
||||
'customer_name' => $enquiry['Customer']['name'],
|
||||
'contact_email' => $enquiry['Contact']['email'],
|
||||
'contact_name' => $enquiry['Contact']['first_name'].' '.$enquiry['Contact']['last_name'],
|
||||
'user_first_name' => $enquiry['User']['first_name'],
|
||||
'user_last_name' => $enquiry['User']['last_name'],
|
||||
'user_email' => $enquiry['User']['email'],
|
||||
'currency_symbol' => $currencySymbol,
|
||||
'show_gst' => (bool)$gst,
|
||||
'commercial_comments' => isset($document['Quote']['commercial_comments']) ? $document['Quote']['commercial_comments'] : '',
|
||||
'line_items' => $lineItems,
|
||||
'pages' => array_map(function($p) { return $p['content']; }, $document['DocPage']),
|
||||
'output_dir' => $outputDir
|
||||
);
|
||||
|
||||
foreach($document['DocPage'] as $page) {
|
||||
$ch = curl_init($goEndpoint);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
|
||||
$pdfdoc->AddPage();
|
||||
$pdfdoc->Page1Header();
|
||||
if($firstPageDone == false) {
|
||||
|
||||
$pdfdoc->DetailsBoxHTML($docTypeFullName, $companyName, $emailTo, $attention, $fromName,
|
||||
$fromEmail, $enquiryNumber, $your_reference, $issue_date, '30');
|
||||
|
||||
$firstPageDone = true;
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlErr = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode < 200 || $httpCode >= 300) {
|
||||
echo "<p>Failed to generate Quote PDF via Go service (HTTP $httpCode).";
|
||||
if ($curlErr) {
|
||||
echo " Error: $curlErr";
|
||||
}
|
||||
|
||||
$pdfdoc->pageContent($page['content']);
|
||||
|
||||
$pdfdoc->Page1Footer();
|
||||
|
||||
$pdfdoc->lastPage();
|
||||
|
||||
echo "</p>";
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
$LineItemTable = $this->element('line_items_table');
|
||||
//echo $LineItemTable;
|
||||
$pdfdoc->SetHeaderMargin(30);
|
||||
$pdfdoc->SetFooterMargin(35);
|
||||
$pdfdoc->SetPrintHeader(true);
|
||||
$pdfdoc->AddPage();
|
||||
//$pdfdoc->MultiCell($w, $h, $txt, $border, $align, $fill, $ln, $x, $y, $reseth, $stretch, $ishtml)
|
||||
|
||||
$pdfdoc->MultiCell(0, 0, 'PRICING & SPECIFICATIONS', 0, 'C', false, 1, null, null, true, false, false);
|
||||
|
||||
|
||||
|
||||
$pdfdoc->pageContent($LineItemTable);
|
||||
$pdfdoc->lastPage();
|
||||
|
||||
if($docType == 'quote') {
|
||||
|
||||
$commercialComments = '<div nobr="true">'.$document['Quote']['commercial_comments'].'</div>';
|
||||
$pdfdoc->pageContent($commercialComments);
|
||||
}
|
||||
|
||||
|
||||
$this->element('pdf_output', array('pdfdoc'=>$pdfdoc));
|
||||
|
||||
|
||||
?>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
|
|
|||
BIN
php/app/views/quotes/.DS_Store
vendored
Normal file
BIN
php/app/views/quotes/.DS_Store
vendored
Normal file
Binary file not shown.
|
|
@ -1,148 +1,60 @@
|
|||
<?php
|
||||
App::import('Vendor','xtcpdf');
|
||||
// AJAX quote generation: call Go quote endpoint instead of TCPDF.
|
||||
|
||||
$goBaseUrl = AppController::getGoBaseUrlOrFail();
|
||||
$goEndpoint = $goBaseUrl . '/go/pdf/generate-quote';
|
||||
|
||||
$outputDir = Configure::read('pdf_directory');
|
||||
|
||||
$tcpdf = new XTCPDF('P', 'mm', 'A4', true, false, 'UTF-8');
|
||||
|
||||
|
||||
$textfont = 'times'; // looks better, finer, and more condensed than 'dejavusans'
|
||||
|
||||
$tcpdf->setHeaderFont(array('times','',10));
|
||||
//$tcpdf->SetLeftMargin(29);
|
||||
//$tcpdf->SetMargins(2, 2);
|
||||
|
||||
|
||||
$tcpdf->SetAuthor("CMC Technologies");
|
||||
$tcpdf->SetTitle("CMC Technologies Quote: ".$quote['Enquiry']['title']);
|
||||
$tcpdf->SetAutoPageBreak( false );
|
||||
|
||||
$tcpdf->xheadercolor = array(150,0,0);
|
||||
$tcpdf->xheadertext = 'CMC TECHNOLOGIES';
|
||||
$tcpdf->xfootertext = 'Copyright © %d CMC Technologies. All rights reserved.';
|
||||
|
||||
$tcpdf->SetHeaderMargin(2);
|
||||
|
||||
$tcpdf->setPrintHeader(false);
|
||||
$tcpdf->setPrintFooter(false);
|
||||
|
||||
|
||||
$pageProducts = $tcpdf->calculateProductPage($quote['Currency'],
|
||||
$enquiry['Enquiry']['gst'], $products, 'LineItem', $commercialDetails);
|
||||
|
||||
|
||||
$page1done = false; //Have we already made the first page? If so, don't show the CMC header/footer
|
||||
|
||||
foreach ($quote['QuotePage'] as $page) {
|
||||
|
||||
$tcpdf->AddPage();
|
||||
|
||||
if($page1done == false) {
|
||||
$tcpdf->Page1Header();
|
||||
|
||||
if( (!$enquiry['Enquiry']['customer_reference']) || ($enquiry['Enquiry']['customer_refernece'] == '') ) {
|
||||
|
||||
$enquiry_date = date('d/m/Y',$time->toUnix($enquiry['Enquiry']['created']));
|
||||
|
||||
|
||||
$enquiry['Enquiry']['customer_reference'] = "Enquiry on $enquiry_date";
|
||||
}
|
||||
|
||||
if ($quote['Quote']['revision'] > 0) {
|
||||
$cmcRef = $enquiry['Enquiry']['title'].' rev '.$quote['Quote']['revision'];
|
||||
}
|
||||
else {
|
||||
$cmcRef = $enquiry['Enquiry']['title'];
|
||||
}
|
||||
|
||||
|
||||
$tcpdf->DetailsBox($enquiry['Customer']['name'], $enquiry['Contact']['email'], $enquiry['Contact']['first_name'].' '.$enquiry['Contact']['last_name'],
|
||||
$enquiry['User']['first_name'].' '.$enquiry['User']['last_name'], $enquiry['User']['email'], $cmcRef,
|
||||
$enquiry['Enquiry']['customer_reference'], $quote['Quote']['date_issued']);
|
||||
|
||||
$lineItems = array();
|
||||
if (isset($products) && is_array($products)) {
|
||||
foreach ($products as $li) {
|
||||
$lineItems[] = array(
|
||||
'item_number' => isset($li['item_number']) ? $li['item_number'] : '',
|
||||
'quantity' => isset($li['quantity']) ? $li['quantity'] : '',
|
||||
'title' => isset($li['title']) ? $li['title'] : '',
|
||||
'unit_price' => isset($li['gross_unit_price']) ? floatval($li['gross_unit_price']) : 0.0,
|
||||
'total_price' => isset($li['gross_price']) ? floatval($li['gross_price']) : 0.0,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$tcpdf->continuedHeader($enquiry['Enquiry']['title']);
|
||||
}
|
||||
|
||||
$payload = array(
|
||||
'document_id' => 0,
|
||||
'cmc_reference' => $quote['Enquiry']['title'],
|
||||
'revision' => intval($quote['Quote']['revision']),
|
||||
'created_date' => date('Y-m-d', strtotime($quote['Enquiry']['created'])),
|
||||
'customer_name' => $quote['Customer']['name'],
|
||||
'contact_email' => $quote['Contact']['email'],
|
||||
'contact_name' => $quote['Contact']['first_name'].' '.$quote['Contact']['last_name'],
|
||||
'user_first_name' => $quote['User']['first_name'],
|
||||
'user_last_name' => $quote['User']['last_name'],
|
||||
'user_email' => $quote['User']['email'],
|
||||
'currency_symbol' => isset($quote['Currency']['symbol']) ? $quote['Currency']['symbol'] : '$',
|
||||
'show_gst' => (bool)$quote['Enquiry']['gst'],
|
||||
'commercial_comments' => isset($commercialDetails) ? $commercialDetails : '',
|
||||
'line_items' => $lineItems,
|
||||
'pages' => array_map(function($p) { return $p['content']; }, $quote['QuotePage']),
|
||||
'output_dir' => $outputDir,
|
||||
);
|
||||
|
||||
$ch = curl_init($goEndpoint);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlErr = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode < 200 || $httpCode >= 300) {
|
||||
echo "<p>Failed to generate Quote PDF via Go service (HTTP $httpCode).";
|
||||
if ($curlErr) {
|
||||
echo " Error: $curlErr";
|
||||
}
|
||||
|
||||
|
||||
|
||||
$currentX = $tcpdf->GetX();
|
||||
$currentY = $tcpdf->GetY();
|
||||
|
||||
$tcpdf->SetTextColor(0);
|
||||
$tcpdf->SetFont('times', '', 12);
|
||||
|
||||
|
||||
$tcpdf->writeHTMLCell(0, 0, $currentX, $currentY+5, $page['content'], '', 1, 0, true, 'L', true);
|
||||
|
||||
|
||||
/* Only show the footer if it's the first page */
|
||||
if($page1done == false) {
|
||||
$tcpdf->Page1Footer();
|
||||
$page1done = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
echo "</p>";
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
print_r($products);
|
||||
|
||||
|
||||
|
||||
//debug($commercialDetails);
|
||||
|
||||
//$tcpdf->productPageHTML($principle_name, $quote['Currency'], $enquiry['Enquiry']['gst'], $products, 'LineItem', $commercialDetails);
|
||||
|
||||
|
||||
|
||||
|
||||
$tcpdf->productPage($quote['Currency'], $enquiry['Enquiry']['gst'], $products, 'LineItem', $commercialDetails,
|
||||
$pageProducts);
|
||||
|
||||
|
||||
|
||||
|
||||
$output_dir = '/Users/karlcordes/Sites/quotenik/app/webroot/pdf/';
|
||||
|
||||
$debuglevel = Configure::read('debug');
|
||||
|
||||
if($debuglevel == 0) {
|
||||
//$output_dir = '/var/www/cakephp/app/webroot/pdf/';
|
||||
}
|
||||
|
||||
|
||||
if($quote['Quote']['revision'] > 0) {
|
||||
$filename = $enquiry['Enquiry']['title'].'rev'.$quote['Quote']['revision'].'.pdf';
|
||||
}
|
||||
else {
|
||||
$filename = $enquiry['Enquiry']['title'].'.pdf';
|
||||
}
|
||||
|
||||
|
||||
$tcpdf->Output($output_dir.$filename, 'F');
|
||||
|
||||
echo "<br> Wrote: ".$output_dir.$filename;
|
||||
|
||||
App::import('Vendor', 'xfpdi');
|
||||
|
||||
|
||||
//$newpdf = new concat_pdf();
|
||||
|
||||
$newpdf = new XFPDI();
|
||||
|
||||
$newpdf->SetMargins(2, 2);
|
||||
$newpdf->setPrintHeader(false);
|
||||
$newpdf->setPrintFooter(false);
|
||||
|
||||
$newpdf->setFiles(array($output_dir.$filename, $output_dir.'CMC_terms_and_conditions2006_A4.pdf'));
|
||||
$newpdf->concat();
|
||||
$newpdf->Output($output_dir.$filename, "F");
|
||||
|
||||
//$tcpdf->Output('cmcquote.pdf', 'D');
|
||||
|
||||
|
||||
?>
|
||||
|
|
@ -1,148 +1,64 @@
|
|||
<?php
|
||||
App::import('Vendor','xtcpdf');
|
||||
// Generate Quote PDF by calling the Go service instead of TCPDF.
|
||||
|
||||
$goBaseUrl = AppController::getGoBaseUrlOrFail();
|
||||
$goEndpoint = $goBaseUrl . '/go/pdf/generate-quote';
|
||||
|
||||
$outputDir = Configure::read('pdf_directory');
|
||||
|
||||
|
||||
$tcpdf = new XTCPDF('P', 'mm', 'A4', true, false, 'UTF-8');
|
||||
|
||||
|
||||
$textfont = 'times'; // looks better, finer, and more condensed than 'dejavusans'
|
||||
|
||||
$tcpdf->setHeaderFont(array('times','',10));
|
||||
//$tcpdf->SetLeftMargin(29);
|
||||
//$tcpdf->SetMargins(2, 2);
|
||||
|
||||
|
||||
$tcpdf->SetAuthor("CMC Technologies");
|
||||
$tcpdf->SetTitle("CMC Technologies Quote: ".$quote['Enquiry']['title']);
|
||||
$tcpdf->SetAutoPageBreak( false );
|
||||
|
||||
$tcpdf->xheadercolor = array(150,0,0);
|
||||
$tcpdf->xheadertext = 'CMC TECHNOLOGIES';
|
||||
$tcpdf->xfootertext = 'Copyright © %d CMC Technologies. All rights reserved.';
|
||||
|
||||
$tcpdf->SetHeaderMargin(2);
|
||||
|
||||
$tcpdf->setPrintHeader(false);
|
||||
$tcpdf->setPrintFooter(false);
|
||||
|
||||
|
||||
$pageProducts = $tcpdf->calculateProductPage($principlesList, $quote['Currency'], $enquiry['Enquiry']['gst'], $products, 'LineItem', $commercialDetails);
|
||||
|
||||
|
||||
$page1done = false; //Have we already made the first page? If so, don't show the CMC header/footer
|
||||
|
||||
foreach ($quote['QuotePage'] as $page) {
|
||||
|
||||
$tcpdf->AddPage();
|
||||
|
||||
if($page1done == false) {
|
||||
$tcpdf->Page1Header();
|
||||
|
||||
if( (!$enquiry['Enquiry']['customer_reference']) || ($enquiry['Enquiry']['customer_refernece'] == '') ) {
|
||||
|
||||
$enquiry_date = date('d/m/Y',$time->toUnix($enquiry['Enquiry']['created']));
|
||||
|
||||
|
||||
$enquiry['Enquiry']['customer_reference'] = "Enquiry on $enquiry_date";
|
||||
}
|
||||
|
||||
if ($quote['Quote']['revision'] > 0) {
|
||||
$cmcRef = $enquiry['Enquiry']['title'].' rev '.$quote['Quote']['revision'];
|
||||
}
|
||||
else {
|
||||
$cmcRef = $enquiry['Enquiry']['title'];
|
||||
}
|
||||
|
||||
|
||||
$tcpdf->DetailsBox($docTypeFullName, $enquiry['Customer']['name'], $enquiry['Contact']['email'], $enquiry['Contact']['first_name'].' '.$enquiry['Contact']['last_name'],
|
||||
$enquiry['User']['first_name'].' '.$enquiry['User']['last_name'], $enquiry['User']['email'], $cmcRef,
|
||||
$enquiry['Enquiry']['customer_reference'], $quote['Quote']['date_issued']);
|
||||
|
||||
}
|
||||
else {
|
||||
$tcpdf->continuedHeader($enquiry['Enquiry']['title']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
$currentX = $tcpdf->GetX();
|
||||
$currentY = $tcpdf->GetY();
|
||||
|
||||
$tcpdf->SetTextColor(0);
|
||||
$tcpdf->SetFont('times', '', 12);
|
||||
|
||||
|
||||
$tcpdf->writeHTMLCell(0, 0, $currentX, $currentY+5, $page['content'], '', 1, 0, true, 'L', true);
|
||||
|
||||
|
||||
/* Only show the footer if it's the first page */
|
||||
if($page1done == false) {
|
||||
$tcpdf->Page1Footer();
|
||||
$page1done = true;
|
||||
$lineItems = array();
|
||||
if (isset($products) && is_array($products)) {
|
||||
foreach ($products as $li) {
|
||||
$lineItems[] = array(
|
||||
'item_number' => isset($li['item_number']) ? $li['item_number'] : '',
|
||||
'quantity' => isset($li['quantity']) ? $li['quantity'] : '',
|
||||
'title' => isset($li['title']) ? $li['title'] : '',
|
||||
'unit_price' => isset($li['gross_unit_price']) ? floatval($li['gross_unit_price']) : 0.0,
|
||||
'total_price' => isset($li['gross_price']) ? floatval($li['gross_price']) : 0.0,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
$payload = array(
|
||||
'document_id' => 0,
|
||||
'cmc_reference' => $enquiry['Enquiry']['title'],
|
||||
'revision' => intval($quote['Quote']['revision']),
|
||||
'created_date' => date('Y-m-d', strtotime($enquiry['Enquiry']['created'])),
|
||||
'customer_name' => $enquiry['Customer']['name'],
|
||||
'contact_email' => $enquiry['Contact']['email'],
|
||||
'contact_name' => $enquiry['Contact']['first_name'].' '.$enquiry['Contact']['last_name'],
|
||||
'user_first_name' => $enquiry['User']['first_name'],
|
||||
'user_last_name' => $enquiry['User']['last_name'],
|
||||
'user_email' => $enquiry['User']['email'],
|
||||
'currency_symbol' => isset($quote['Currency']['symbol']) ? $quote['Currency']['symbol'] : '$',
|
||||
'show_gst' => (bool)$enquiry['Enquiry']['gst'],
|
||||
'commercial_comments' => isset($commercialDetails) ? $commercialDetails : '',
|
||||
'line_items' => $lineItems,
|
||||
'pages' => array_map(function($p) { return $p['content']; }, $quote['QuotePage']),
|
||||
'output_dir' => $outputDir,
|
||||
);
|
||||
|
||||
$ch = curl_init($goEndpoint);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
|
||||
//print_r($products);
|
||||
print_r($principlesList);
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlErr = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
|
||||
//debug($commercialDetails);
|
||||
|
||||
//$tcpdf->productPageHTML($principle_name, $quote['Currency'], $enquiry['Enquiry']['gst'], $products, 'LineItem', $commercialDetails);
|
||||
|
||||
|
||||
|
||||
|
||||
$tcpdf->productPage($principlesList, $quote['Currency'], $enquiry['Enquiry']['gst'], $products, 'LineItem', $commercialDetails,
|
||||
$pageProducts);
|
||||
|
||||
|
||||
//print_r($products);
|
||||
|
||||
|
||||
$output_dir = '/Users/karlcordes/Sites/quotenik/app/webroot/pdf/';
|
||||
|
||||
$debuglevel = Configure::read('debug');
|
||||
|
||||
if($debuglevel == 0) {
|
||||
$output_dir = '/var/www/cakephp/app/webroot/pdf/';
|
||||
if ($httpCode < 200 || $httpCode >= 300) {
|
||||
echo "<p>Failed to generate Quote PDF via Go service (HTTP $httpCode).";
|
||||
if ($curlErr) {
|
||||
echo " Error: $curlErr";
|
||||
}
|
||||
echo "</p>";
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
if($quote['Quote']['revision'] > 0) {
|
||||
$filename = $enquiry['Enquiry']['title'].'rev'.$quote['Quote']['revision'].'.pdf';
|
||||
}
|
||||
else {
|
||||
$filename = $enquiry['Enquiry']['title'].'.pdf';
|
||||
}
|
||||
|
||||
|
||||
$tcpdf->Output($output_dir.$filename, 'F');
|
||||
|
||||
echo "<br> Wrote: ".$output_dir.$filename;
|
||||
|
||||
//$tcpdf->Output('cmcquote.pdf', 'D');
|
||||
|
||||
App::import('Vendor', 'xfpdi');
|
||||
|
||||
|
||||
//$newpdf = new concat_pdf();
|
||||
|
||||
$newpdf = new XFPDI();
|
||||
|
||||
$newpdf->SetMargins(2, 2);
|
||||
$newpdf->setPrintHeader(false);
|
||||
$newpdf->setPrintFooter(false);
|
||||
|
||||
$newpdf->setFiles(array($output_dir.$filename, $output_dir.'CMC_terms_and_conditions2006_A4.pdf'));
|
||||
$newpdf->concat();
|
||||
$newpdf->Output($output_dir.$filename, "F");
|
||||
|
||||
?>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.location.replace("/quotes/view/<?=$quote['Quote']['id']?>");
|
||||
</script>
|
||||
Loading…
Reference in a new issue