Commit Graph

29 Commits

Author SHA1 Message Date
jordi fita mas fa57c4b191 Refactor inline tag edit form into its own file
I was repeating myself a lot for this use case, because each one needed
a different URL and SQL query, however they were kind of structurally
similar and could be refactored into common functions.
2024-08-15 04:18:18 +02:00
jordi fita mas 835e52dbcb Return HTTP 404 instead of 500 for invalid UUID values in URL
Since most of PL/pgSQL functions accept a `uuid` domain, we get an error
if the value is not valid, forcing us to return an HTTP 500, as we
can not detect that the error was due to that.

Instead, i now validate that the slug is indeed a valid UUID before
attempting to send it to the database, returning the correct HTTP error
code and avoiding useless calls to the database.

I based the validation function of Parse() from Google’s uuid package[0]
because this function is an order or magnitude faster in benchmarks:

  goos: linux
  goarch: amd64
  pkg: dev.tandem.ws/tandem/numerus/pkg
  cpu: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz
  BenchmarkValidUuid-4            36946050                29.37 ns/op
  BenchmarkValidUuid_Re-4          3633169               306.70 ns/op

The regular expression used for the benchmark was:

  var re = regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$")

And the input parameter for both functions was the following valid UUID,
because most of the time the passed UUID will be valid:

  "f47ac10b-58cc-0372-8567-0e02b2c3d479"

I did not use the uuid package, even though it is in Debian’s
repository, because i only need to check whether the value is valid,
not convert it to a byte array.  As far as i know, that package can not
do that.

[0]: https://github.com/google/uuid
2023-07-17 12:07:23 +02:00
jordi fita mas 5e8bed8452 Add reset button to filters
I want this button, as well as the submit button, to be on a row below
the filters’ input, especially for quotes and invoices, that have the
most filters and looks weird with the button wedged in.  Thus, i added
a <fieldset> around all the filters.

Closes #69
2023-07-16 20:56:11 +02:00
jordi fita mas 07a28639f2 There is no need for array_to_string() in tags 2023-05-27 21:36:10 +02:00
jordi fita mas 27a266097a Do not use pointer to point when retrieving tags to edit
It is the same with go 16, but with Debian’s go 15 it fails and
I believe, but i am not sure, this is the reason.
2023-05-27 20:51:36 +02:00
jordi fita mas 856ddde00e Add the inline form for product tags 2023-05-09 12:18:31 +02:00
jordi fita mas 4a9c3748cd Guard a StatusUnprocessableEntity with check for HTMx request
Otherwise, HTMx would just ignore the HTML returned by the server and
dispatch an error.
2023-05-05 10:57:48 +02:00
jordi fita mas 73497eb051 Add missing return on not found for edit product 2023-05-05 10:54:40 +02:00
jordi fita mas f2a0cd7d94 Move back most dialogs to regular (but still boosted) pages
With Oriol agreed that adding or editing invoices, products, and
contacts is not just a “user interruption” but the main flow of the
program, and, as such, it is not correct to use dialogs for these.

More importantly, it was harder to concentrate, specially with the more
involved form of invoices, because of all the “noise” behind the dialog.
2023-04-25 15:28:55 +02:00
jordi fita mas c5fba3246e Fix label for products’ name filter 2023-04-25 15:21:34 +02:00
jordi fita mas 7d895fe5f9 Use HTMx to add product rows “inline” in the invoice form
I actually find more comfortable to select the product from the list
presented up until now, but this is mostly because i have very few
products and the list is not too long, so the idea is that with
JavaScript we will dynamically add an empty product row to the invoice
and then use the name field to search the product by name.

I have the feeling that i am doing something wrong because i ended up
with a lot of HTMx attribute for what i feel is not that much work,
but for now it will work.

I have added the `Is` field to `InputField` in order to include the `id`
attribute to the HTML element, because the HTMLAttributes are attached
to the `input`, not the `div`, and i felt like this one should also be
a custom element based on div, like all the others.

These is not yet any keyboard control to select the search results.

I am not happy with having the search of products in a different URL
than the index, specially since they use the exact same SQL query and
ProductFilter struct, but i did not know how else ask for a different
representation without resorting to the more complicated MIME types.
2023-04-24 02:00:38 +02:00
jordi fita mas 2ced61d304 Add the product filter form
I need a way to search products by name in the invoice form, when the
user adds or changes a product.  Since this is something that i have to
add too to the product list, i added it now so the function will already
be ready.
2023-04-23 03:20:01 +02:00
jordi fita mas bc48dd4089 Replace tag relations with array attributes
It all started when i wanted to try to filter invoices by multiple tags
using an “AND”, instead of “OR” as it was doing until now.  But
something felt off and seemed to me that i was doing thing much more
complex than needed, all to be able to list the tags as a suggestion
in the input field—which i am not doing yet.

I found this article series[0] exploring different approaches for
tagging, which includes the one i was using, and comparing their
performance.  I have not actually tested it, but it seems that i have
chosen the worst option, in both query time and storage.

I attempted to try using an array attribute to each table, which is more
or less the same they did in the articles but without using a separate
relation for tags, and i found out that all the queries were way easier
to write, and needed two joins less, so it was a no-brainer.

[0]: http://www.databasesoup.com/2015/01/tag-all-things.html
2023-04-07 21:31:35 +02:00
jordi fita mas 2df6947577 Create constants for the HTMX request and response headers used
It works better with an IDE, and less chance of mistyping something
without notice.
2023-04-06 12:07:20 +02:00
jordi fita mas 47c23fc4cc Boost the products’ section links and forms
Had to add the editProductPage because now i need to know the slug in
order to build the form’s action link.  I also added the `ProductName`
field because it was less awkward than using `.Form.Name` everywhere.
2023-03-27 09:44:04 +02:00
jordi fita mas a1f70ff654 Add tags for products too
With Oriol we agreed that products should have tags, too, and that the
“tag pool”, as it were, should be shared with the one for invoices and
contacts.

Had to add the `company_id` attribute in the `using` clause for `tag` in
`MustFillFromDatabase`, even though it’s not strictly necessary, because
then PostgreSQL does not know which `company_id` attribute use for the
join with `company`—the one from `product` or the one from `tag`.
2023-03-26 13:51:57 +02:00
jordi fita mas 41ce5af2ed Boost the main navigation links with HTMx
I am not sure if, at the end, all pages that now use
mustRenderAppTemplate will be replaced with mustRenderMainTemplate,
but for now i keep them separate to know which routes are already
“boosted”.
2023-03-23 10:55:02 +01:00
jordi fita mas e11a3c57f5 Add validation of single tax from each class
We only allow a single tax of each class in products and invoices,
because it does not make sense to add two different VAT to the same
product, for instance.
2023-03-01 11:55:26 +01:00
jordi fita mas fffab62135 Use left join between products and taxes
A product can have no tax.
2023-02-25 03:20:34 +01:00
jordi fita mas 4463c7ee0b Use array_agg to get the taxes for the product’s form 2023-02-14 12:49:29 +01:00
jordi fita mas 4db0a8fb5a Refactor checking for pgx.ErrNoRows in a function 2023-02-14 12:46:11 +01:00
jordi fita mas 13fa1d6b89 Add PL/pgSQL functions to add and edit products
I am going to add similar functions for invoices, as i will need to
add the taxes for their products and their own taxes, thus the Go code
will begin to be “too much work” and i feel better if that is in
PL/pgSQL.

If i have these functions for invoices, there is no point on having to
do almost the same work, albeit less, for products.
2023-02-14 12:39:54 +01:00
jordi fita mas 4903c8a3b9 Add the form to add products to an invoice and create invoices too
Still missing: the invoice number, that requires more tables and
possibly a PL/pgSQL function to do it properly.
2023-02-12 21:06:48 +01:00
jordi fita mas 72fbed68ac Add a missing StatusUnprocessableEntity HTTP response code 2023-02-12 21:03:46 +01:00
jordi fita mas 4be2597a86 Allow multiple taxes, and even not tax, for products
It seems that we do not agree en whether the IRPF tax should be
something of the product or the contact, so we decided to make the
product have multiple taxes, just in case, and if only one is needed,
then users can just select one; no need to limit to one.
2023-02-08 13:47:36 +01:00
jordi fita mas 73ca559209 Add template for InputField of type textarea 2023-02-07 15:28:22 +01:00
jordi fita mas ae1949024b Allow optional select with empty label
This is not yet necessary, but the empty label is because i do not want
to select a default tax for products—at least, not without a setting for
it.

Since i need to add the required attribute now to select, because
otherwise the browser would allow sending that empty value, i did not
want to do it unconditionally, just in case.
2023-02-05 14:06:33 +01:00
jordi fita mas 60f9792e58 Convert from cents to “price” and back
I do not want to use floats in the Go lang application, because it is
not supposed to do anything with these values other than to print and
retrieve them from the user; all computations will be performed by
PostgreSQL in cents.

That means i have to “convert” from the price format that users expect
to see (e.g., 1.234,56) to cents (e.g., 123456) and back when passing
data between Go and PostgreSQL, and that conversion depends on the
currency’s decimal places.

At first i did everything in Go, but saw that i would need to do it in
a loop when retrieving the list of products, and immediately knew it was
a mistake—i needed a PL/pgSQL function for that.

I still need to convert from string to float, however, when printing the
value to the user.  Because the string representation is in C, but i
need to format it according to the locale with golang/x/text.  That
package has the information of how to correctly format numbers, but it
is in an internal package that i can not use, and numbers.Digit only
accepts numeric types, not a string.
2023-02-05 13:55:12 +01:00
jordi fita mas e9cc331ee0 Add products section
There is still some issues with the price field, because for now it is
in cents, but do not have time now to fix that.
2023-02-04 11:32:39 +01:00