Compare commits

..

3 Commits

Author SHA1 Message Date
jordi fita mas 1c23cbb7d7 Add the customer name to the invoice’s PDF file name
This was requested by Oriol; there are no other technical or legal
requirements for this.

I can not simply append the customer name to the file because it could
have characters that are not valid in file name depending on the
operating system, so i have to “slugify” it.

Closes #65
2023-07-07 11:32:59 +02:00
jordi fita mas 7644a3a17a Move data-hx-boost inside the <nav> for quotes and invoices
This is because the download button must *not* be boosted, or HTMx would
just slap the binary data into the document as is, without downloading
it.
2023-07-07 11:16:43 +02:00
jordi fita mas 8c31fc63c5 Keep products in invoices/quotes sorted by (roughly) insertion order
There was no explicit `order by` in the queries that list the products
of quotes and invoices, so PostgreSQL was free to use any order it
wanted.  In this case, since was am grouping first by name, the result
was sorted by product name.

This is not an issue in most cases, albeit a bit rude to the user,
except for when the products *have* to in the same order the user
entered them, because they are monthly fees or something like that, that
must be ordered by month _number_, not by their _name_; the user will
usually input them in the correct order they want them on the invoice or
quote.

Sorting by *_product_id does *not* guarantee that they will always be
in insertion order, because the sequence can “wrap”, but i think i am
going to have bigger problems at that point.
2023-07-07 10:55:41 +02:00
9 changed files with 39 additions and 10 deletions

3
debian/control vendored
View File

@ -10,8 +10,9 @@ Build-Depends:
golang-github-jackc-pgx-v4-dev,
golang-github-julienschmidt-httprouter-dev,
golang-github-leonelquinteros-gotext-dev,
golang-golang-x-text-dev,
golang-github-rainycape-unidecode-dev,
golang-github-tealeg-xlsx-dev,
golang-golang-x-text-dev,
postgresql-all (>= 217~),
sqitch,
pgtap,

1
go.mod
View File

@ -7,6 +7,7 @@ require (
github.com/jackc/pgx/v4 v4.15.0
github.com/julienschmidt/httprouter v1.3.0
github.com/leonelquinteros/gotext v1.5.0
github.com/rainycape/unidecode v0.0.0-20150906181237-c9cf8cdbbfe8
github.com/tealeg/xlsx v0.0.0-20181024002044-dbf71b6a931e
golang.org/x/text v0.7.0
)

2
go.sum
View File

@ -92,6 +92,8 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rainycape/unidecode v0.0.0-20150906181237-c9cf8cdbbfe8 h1:iZTHFqK/oFrjyFDkiw5U/RjQxkMlkpq6tHQIO407i+s=
github.com/rainycape/unidecode v0.0.0-20150906181237-c9cf8cdbbfe8/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=

View File

@ -658,7 +658,7 @@ func mustWriteInvoicesPdf(r *http.Request, slugs []string) []byte {
if inv == nil {
continue
}
f, err := w.Create(inv.Number + ".pdf")
f, err := w.Create(fmt.Sprintf("%s-%s.pdf", inv.Number, slugify(inv.Invoicee.Name)))
if err != nil {
panic(err)
}

22
pkg/slug.go Normal file
View File

@ -0,0 +1,22 @@
package pkg
import (
"github.com/rainycape/unidecode"
"regexp"
"strings"
)
var (
nonValidChars = regexp.MustCompile("[^a-z0-9-_]")
multipleDashes = regexp.MustCompile("-+")
)
func slugify(s string) (slug string) {
slug = strings.TrimSpace(s)
slug = unidecode.Unidecode(slug)
slug = strings.ToLower(slug)
slug = nonValidChars.ReplaceAllString(slug, "-")
slug = multipleDashes.ReplaceAllString(slug, "-")
slug = strings.Trim(slug, "-_")
return slug
}

View File

@ -86,6 +86,9 @@ func mustRenderTemplate(wr io.Writer, r *http.Request, layout string, filename s
sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
return humanizeBytes(bytes, 1024, sizes)
},
"slugify": func(s string) string {
return slugify(s)
},
})
if _, err := t.ParseFiles(templateFile(filename), templateFile(layout), templateFile("form.gohtml")); err != nil {
panic(err)

View File

@ -4,8 +4,8 @@
{{ define "breadcrumbs" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.InvoicesIndexPage*/ -}}
<nav data-hx-target="main" data-hx-boost="true">
<p>
<nav data-hx-target="main">
<p data-hx-boost="true">
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
<a>{{( pgettext "Invoices" "title" )}}</a>
</p>
@ -17,7 +17,7 @@
<button type="submit"
name="action" value="download"
>{{( pgettext "Download invoices" "action" )}}</button>
<a class="primary button"
<a class="primary button" data-hx-boost="true"
href="{{ companyURI "/invoices/new" }}">{{( pgettext "New invoice" "action" )}}</a>
</p>
</form>
@ -104,7 +104,7 @@
<td class="numeric">{{ .Total|formatPrice }}</td>
{{- $title = .Number | printf (pgettext "Download invoice %s" "action") -}}
<td class="invoice-download"><a href="{{ companyURI "/invoices/"}}{{ .Slug }}.pdf"
download="{{ .Number}}.pdf"
download="{{ .Number}}-{{ .CustomerName | slugify}}.pdf"
title="{{( pgettext "Download invoice" "action" )}}"
aria-label="{{ $title }}"><i
class="ri-download-line"></i></a></td>

View File

@ -19,7 +19,7 @@
href="{{ companyURI "/invoices/"}}{{ .Slug }}/edit">{{( pgettext "Edit" "action" )}}</a>
<a class="primary button"
href="{{ companyURI "/invoices/" }}{{ .Slug }}.pdf"
download="{{ .Number}}.pdf">{{( pgettext "Download invoice" "action" )}}</a>
download="{{ .Number}}-{{ .Invoicee.Name | slugify }}.pdf">{{( pgettext "Download invoice" "action" )}}</a>
</p>
</nav>
{{- end }}

View File

@ -4,8 +4,8 @@
{{ define "breadcrumbs" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.QuotesIndexPage*/ -}}
<nav data-hx-target="main" data-hx-boost="true">
<p>
<nav data-hx-target="main">
<p data-hx-boost="true">
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
<a>{{( pgettext "Quotations" "title" )}}</a>
</p>
@ -17,7 +17,7 @@
<button type="submit"
name="action" value="download"
>{{( pgettext "Download quotations" "action" )}}</button>
<a class="primary button"
<a class="primary button" data-hx-boost="true"
href="{{ companyURI "/quotes/new" }}">{{( pgettext "New quotation" "action" )}}</a>
</p>
</form>