From 8529da161530344d9a6b3aa467153cd470dc0b99 Mon Sep 17 00:00:00 2001 From: jordi fita mas Date: Mon, 29 May 2023 00:01:11 +0200 Subject: [PATCH] Use HTMx to delete and restore invoice products MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is better that way because it works without JavaScript; if HTMx is not available, it will just use regulars forms. The problem is that most of the submit buttons where using formaction to send the request to a different action, and only one button was the “real” action. Since i could not pass the formaction to invoice-product-form template, i have changed the “default” action to the one with “ancillary” functions. I have to use a different action to remove for each product because i can not pass the index to the backend without JavaScript: it only depends on the button click, that already has a name for the action. Thus, in a way, i have “merged” the action and the index in a single name. --- pkg/invoices.go | 91 ++++++++++++++++++++++++++----- web/template/form.gohtml | 6 +- web/template/invoices/edit.gohtml | 43 +++++++++++---- web/template/invoices/new.gohtml | 43 +++++++++++---- 4 files changed, 145 insertions(+), 38 deletions(-) diff --git a/pkg/invoices.go b/pkg/invoices.go index b19b638..07bf53a 100644 --- a/pkg/invoices.go +++ b/pkg/invoices.go @@ -20,6 +20,8 @@ import ( "time" ) +const removedProductSuffix = ".removed" + type InvoiceEntry struct { Slug string Date time.Time @@ -553,16 +555,17 @@ func mustWriteInvoicesPdf(r *http.Request, slugs []string) []byte { } type invoiceForm struct { - locale *Locale - company *Company - Number string - InvoiceStatus *SelectField - Customer *SelectField - Date *InputField - Notes *InputField - PaymentMethod *SelectField - Tags *TagsField - Products []*invoiceProductForm + locale *Locale + company *Company + Number string + InvoiceStatus *SelectField + Customer *SelectField + Date *InputField + Notes *InputField + PaymentMethod *SelectField + Tags *TagsField + Products []*invoiceProductForm + RemovedProduct *invoiceProductForm } func newInvoiceForm(ctx context.Context, conn *Conn, locale *Locale, company *Company) *invoiceForm { @@ -665,6 +668,25 @@ func (form *invoiceForm) Update() { } } +func (form *invoiceForm) RemoveProduct(index int) { + products := form.Products + form.Products = nil + for n, product := range products { + if n == index { + form.RemovedProduct = product + } else { + if n != len(form.Products) { + product.Index = len(form.Products) + product.Rename() + } + form.Products = append(form.Products, product) + } + } + if form.RemovedProduct != nil { + form.RemovedProduct.RenameWithSuffix(removedProductSuffix) + } +} + const selectProductBySlug = ` select '' , product_id @@ -708,6 +730,23 @@ func (form *invoiceForm) mustAddProductsFromQuery(ctx context.Context, conn *Con } } +func (form *invoiceForm) InsertProduct(product *invoiceProductForm) { + replaced := false + for n, existing := range form.Products { + if existing.Quantity.Val == "" || existing.Quantity.Val == "0" { + product.Index = n + form.Products[n] = product + replaced = true + break + } + } + if !replaced { + product.Index = len(form.Products) + form.Products = append(form.Products, product) + } + product.Rename() +} + func (form *invoiceForm) MustFillFromDatabase(ctx context.Context, conn *Conn, slug string) bool { var invoiceId int selectedInvoiceStatus := form.InvoiceStatus.Selected @@ -834,7 +873,9 @@ func newInvoiceProductForm(index int, company *Company, locale *Locale, taxOptio } func (form *invoiceProductForm) Rename() { - suffix := "." + strconv.Itoa(form.Index) + form.RenameWithSuffix("." + strconv.Itoa(form.Index)) +} +func (form *invoiceProductForm) RenameWithSuffix(suffix string) { form.InvoiceProductId.Name = "product.invoice_product_id" + suffix form.ProductId.Name = "product.id" + suffix form.Name.Name = "product.name" + suffix @@ -1018,7 +1059,8 @@ func handleInvoiceAction(w http.ResponseWriter, r *http.Request, action string, http.Error(w, err.Error(), http.StatusForbidden) return } - switch r.Form.Get("action") { + actionField := r.Form.Get("action") + switch actionField { case "update": form.Update() w.WriteHeader(http.StatusOK) @@ -1030,8 +1072,31 @@ func handleInvoiceAction(w http.ResponseWriter, r *http.Request, action string, form.AddProducts(r.Context(), conn, r.Form["slug"]) w.WriteHeader(http.StatusOK) renderForm(w, r, form) + case "restore-product": + restoredProduct := newInvoiceProductForm(0, company, locale, mustGetTaxOptions(r.Context(), conn, company)) + restoredProduct.RenameWithSuffix(removedProductSuffix) + if err := restoredProduct.Parse(r); err != nil { + panic(err) + } + form.InsertProduct(restoredProduct) + form.Update() + w.WriteHeader(http.StatusOK) + renderForm(w, r, form) default: - http.Error(w, gettext("Invalid action", locale), http.StatusBadRequest) + prefix := "remove-product." + if strings.HasPrefix(actionField, prefix) { + index, err := strconv.Atoi(actionField[len(prefix):]) + if err != nil { + http.Error(w, gettext("Invalid action", locale), http.StatusBadRequest) + } else { + form.RemoveProduct(index) + form.Update() + w.WriteHeader(http.StatusOK) + renderForm(w, r, form) + } + } else { + http.Error(w, gettext("Invalid action", locale), http.StatusBadRequest) + } } } diff --git a/web/template/form.gohtml b/web/template/form.gohtml index 6235c76..3421438 100644 --- a/web/template/form.gohtml +++ b/web/template/form.gohtml @@ -145,13 +145,13 @@ {{ define "invoice-product-form" -}} {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.invoiceProductForm*/ -}}
- {{ template "hidden-field" .InvoiceProductId }} diff --git a/web/template/invoices/edit.gohtml b/web/template/invoices/edit.gohtml index a6a1c66..1e4f02c 100644 --- a/web/template/invoices/edit.gohtml +++ b/web/template/invoices/edit.gohtml @@ -17,18 +17,38 @@ {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editInvoicePage*/ -}}

{{ printf (pgettext "Edit Invoice “%s”" "title") .Number }}

-
+ {{ csrfToken }} {{ with .Form -}} -
- {{ template "select-field" .Customer }} - {{ template "hidden-field" .Date }} - {{ template "tags-field" .Tags }} - {{ template "select-field" .PaymentMethod }} - {{ template "select-field" .InvoiceStatus }} - {{ template "input-field" .Notes }} -
+ {{ if .RemovedProduct -}} +
+ {{ with .RemovedProduct -}} +

{{printf (gettext "Product “%s” removed") .Name}}

+ + {{ template "hidden-field" .InvoiceProductId }} + {{ template "hidden-field" .ProductId }} + {{ template "hidden-field" .Name }} + {{ template "hidden-field" .Price }} + {{ template "hidden-field" .Quantity }} + {{ template "hidden-field" .Discount }} + {{ template "hidden-field" .Description }} + {{ template "hidden-select-field" .Tax }} + {{- end }} +
+ {{- end }} + +
+ {{ template "select-field" .Customer }} + {{ template "hidden-field" .Date }} + {{ template "tags-field" .Tags }} + {{ template "select-field" .PaymentMethod }} + {{ template "select-field" .InvoiceStatus }} + {{ template "input-field" .Notes }} +
{{- range $product := .Products }} {{ template "invoice-product-form" . }} @@ -55,18 +75,19 @@
- -
diff --git a/web/template/invoices/new.gohtml b/web/template/invoices/new.gohtml index 0563e7d..a3013ca 100644 --- a/web/template/invoices/new.gohtml +++ b/web/template/invoices/new.gohtml @@ -17,18 +17,38 @@ {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newInvoicePage*/ -}}

{{(pgettext "New Invoice" "title")}}

- + {{ csrfToken }} {{ with .Form -}} -
- {{ template "hidden-select-field" .InvoiceStatus }} - {{ template "select-field" .Customer }} - {{ template "input-field" .Date }} - {{ template "tags-field" .Tags }} - {{ template "select-field" .PaymentMethod }} - {{ template "input-field" .Notes }} -
+ {{ if .RemovedProduct -}} +
+ {{ with .RemovedProduct -}} +

{{printf (gettext "Product “%s” removed") .Name}}

+ + {{ template "hidden-field" .InvoiceProductId }} + {{ template "hidden-field" .ProductId }} + {{ template "hidden-field" .Name }} + {{ template "hidden-field" .Price }} + {{ template "hidden-field" .Quantity }} + {{ template "hidden-field" .Discount }} + {{ template "hidden-field" .Description }} + {{ template "hidden-select-field" .Tax }} + {{- end }} +
+ {{- end }} + +
+ {{ template "hidden-select-field" .InvoiceStatus }} + {{ template "select-field" .Customer }} + {{ template "input-field" .Date }} + {{ template "tags-field" .Tags }} + {{ template "select-field" .PaymentMethod }} + {{ template "input-field" .Notes }} +
{{- range $product := .Products }} {{ template "invoice-product-form" . }} {{- end }} @@ -54,18 +74,19 @@
- -