Commit Graph

195 Commits

Author SHA1 Message Date
jordi fita mas a5dc434aa2 Boost the links in the invoice table 2023-03-28 09:57:48 +02:00
jordi fita mas 2417b4ebd2 Remove the link to edit contact from the invoice table
We agreed with Oriol that this link would only serve to confuse people.
I initially added the link because i thought it was a shame to have to
navigate to the contact section to look or change the info of a customer
that you have an invoice for in front of you.  However, it makes little
sense to be able to edit the contact from both sections, and we do not
have a “view page” for contacts to link to, thus the removal.
2023-03-28 09:50:19 +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 2086f68bd8 Boost contact’s tag links to update <main> with the filtered list 2023-03-27 09:40:24 +02:00
jordi fita mas 5d82597d14 Manually update and restore the <title> when showing dialog with HTMx
Mostly, the same problem as before: if the document title does not
reflect the application’s state, it becomes useless when there are
multiple open tabs.

In this case, however, i do not know how to tell HTMx to restore the
title to how it was before opening the dialog without a new request to
the server, that makes no sense when the dialog was closed without any
change whatsoever.  Thus, i do it with JavaScript on the client side.
2023-03-26 14:06:26 +02:00
jordi fita mas b0db8df732 Add the <title> element for the “main” boosted pages
Otherwise, the page title does not change and becomes useless.
2023-03-26 13:57:24 +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 4131602fa3 Add tags for contacts too
With Oriol we agreed that contacts should have tags, too, and that the
“tag pool”, as it were, should be shared with the one for invoices (and
all future tags we might add).

I added the contact_tag relation and tag_contact function, just like
with invoices, and then realized that the SQL queries that Go had to
execute were becoming “complex” enough: i had to get not only the slug,
but the contact id to call tag_contact, and all inside a transaction.

Therefore, i opted to create the add_contact and edit_contact functions,
that mirror those for invoice and products, so now each “major” section
has these functions.  They also simplified a bit the handling of the
VATIN and phone numbers, because it is now encapsuled inside the
PL/pgSQL function and Go does not know how to assemble the parts.
2023-03-26 01:32:53 +01:00
jordi fita mas 6b73acafe6 Add SQL and helper PL/pgSQL functions to tag invoices
We plan to tag also contacts and products using the same tag relation,
but different invoice_tag, contact_tag, and product_tag relations for
each one.  However, the logic is the same for all three, hence it makes
more sense to put it into a PL/pgSQL with dynamic SQL.  Moreover, the
SQL for tagging in add_invoice and edit_invoice where almost exactly
the same, the only difference was deleting the existing tags when
editing.

I do not execute the tag_relation function in its test suite because
by itself it does nothing without supporting invoice_tag, contact_tag,
or any such relation, so it is being tested in the suite for
tag_invoice.
2023-03-26 00:18:29 +01:00
jordi fita mas 7e8ec539ff Add a SnackBar to show HTMx errors
We do not have any design yet for errors and other notifications, so i
followed material design, for now, since we already kind of use their
input fields design.

This time i decided to use AlpineJS because there is not that much HTML
code, and the transitioning is way easier to do in AlpineJS than it
would be with plain JavaScript—not to mention the bugs i would
introduce.
2023-03-25 01:56:26 +01: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 6e081a1846 Put the edit contact form into a dialog with HTMx
Had to change the data context for that template to include the Slug,
so that the <form> element can set the correct `action` instead of
using the current URI, as it is no longer “correct” (form-wise) when
using HTMx.
2023-03-23 10:46:14 +01:00
jordi fita mas b07fe6cfa2 Show the add contact form in a modal dialog 2023-03-22 14:59:54 +01:00
jordi fita mas b1e3afc48b Show the tax details form in a dialog using HTMx 2023-03-21 11:58:54 +01:00
jordi fita mas 9e757cb9f4 Show the profile form in a dialog using HTMx
Had to split the actual page content and the breadcrumbs because they
do not belong in a dialog.  However, i had to change all templates to
do that.
2023-03-20 13:09:52 +01:00
jordi fita mas 82eb8a2733 Start the tag input custom element
This is more or less the same as a multiselect, except that now it
adds a list of string element that you write into the search element.

It is supposed to fetch a list of tag suggestions from the server, but i
have not implemented it yet.
2023-03-19 23:11:40 +01:00
jordi fita mas 356d0a0892 Handle case of no tag given for invoice
In that case, strings.Split() return an array with a single empty string
element, that does not pass the domain check for tag_name in the
database.

And an invoice with no tags would get an array of a single NULL in
array_agg, so i had to convert it to an empty string in order for it
to work as expected.
2023-03-19 23:10:01 +01:00
jordi fita mas 041017adc3 Add missing ARIA attributes and keyboard controls to multiselect
I use MDN’s documentation[0] as guid for both.

[0]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/combobox_role
2023-03-18 07:17:28 +01:00
jordi fita mas 2dde25c862 Reimplement the multiselect as a custom element
What i really set off on was to refactor the multiselect’s x-data
context to a separate JavaScript file.

I did not see the need at first, thinking that it would not matter
because it was used only in a template and i was not duplicating the
code in my files.  However, i then realized that having the context
in the template means the visitor has to download it each and every time
it accesses a form with a multiselect, even if nothing changed, and,
worse, it would download it multiple times if there were many
multiselect controls.

It makes more sense to put all that into a file that the browser would
only download and parse once, if the proper caching is set.

Once i realized that, it was a shame that AlpineJS has no way to do
the same for the HTML structure[0], for the exact same reasons: not
wanting to download many times the same extra <template> and other
markup required to build the control for JavaScript users.  And then i
remembered that this is supposed to be custom element’s main selling
point.

At first i tried to create a shadow DOW to replace the <select> with
the same <div> and <ul> that i used with Alpine, but it turns out that
<select> is not one of the allowed elements that can have a shadow root
attached[0].

Therefore, i changed the custom element to extend the <div> for the
<select> and <label> instead—the same element that had the x-init
context—, but i would have to define or include all the styles inside
the shadow DOM, and bring the lang attribute, for it to look like it
did before.   Out with the shadow DOM, and modify the <div>’s contents
instead.

At this point the code was so far removed from the declarative way that
AlpineJS promotes that i did not see much value on using it, except for
its reactivity.   But, given that this is such a small component, at the
end decided to write it all in plain JavaScript.

It is more code, at least looking only at the code i had to write, but
i love how i only have to add an is="numerus-multiselect" attribute to
HTML for it to work.

[0]: https://github.com/alpinejs/alpine/discussions/1205
[1]: https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow
2023-03-17 14:55:12 +01:00
jordi fita mas 1c9fe14ab9 Improve multiselect’s usability with keyboard handling
Had to replace the tags <ul> with a div with an input, so that the
browser can focus the keywoard there.  For now i do not have a
focus-within CSS rule because we do no yet have a style for focus
highlight.

I have replaced the template for-loop to fill the options with the
JavaScript equivalent for two reasons.  The first is that GoLand is very
stupid and can not handle that templating code inside the JavaScript
function and complains of non-existing problemes all the time.

The second is that, taking advantage of the input, i now have filtering
of options and have to remove accents from the label and convert it to
lowercase into a separate property just for that.  I could do that with
a Go function, but it is something that i also have to do for the
input’s value when it changes, therefore i am forced to use JavaScript
and, if i am already using it for one string, it makes no sense to have
duplicate functionality in Go code.

The control still has missing aria attributes, and the list of options
is not yet navigable with the keyboard.
2023-03-16 12:52:44 +01:00
jordi fita mas f93d557aa9 Move the multiselect “component” to the select-field template
I had in the product edit page only because it was easier to test there
while i was developing it, but it is something that should be done for
all select[multiple], of course.

I removed the whole x-cloak thing because i am not sure what would
happen if i do something wrong and Alpine can not initialize the
multiselect; probably show nothing to the user.  Now it shows the
native select a fraction of a second, but if i fuck it up at least the
user can still use the app.
2023-03-15 11:44:18 +01:00
jordi fita mas 5702f0d198 Start “improving” the user interface with AlpineJS: tax selector
It is a shitty component, but i do not have more time today to do it
better.
2023-03-14 18:07:38 +01:00
jordi fita mas 8efae0485e Add the edit form for invoices
I had to change the way /invoices/new and /invoices/batch are handled,
because httprouter was not happy with the new POST /invoices/:slug/edit
route, claiming that /invoices/:slug conflicts with the previously
existing routes.

I also could not make it work with the PATCH method, even though i
correctly added the patchMethod override function, therefore editing
invoices is also weird because i have to take into account the “quick”
invoice status change.

I use the same form for both new and edit invoices, because the only
changes are that we can not edit the invoice date and number, by
Oriol’s design, but must be able to change the status; very similar
forms.
2023-03-13 15:00:35 +01:00
jordi fita mas 1ab48d2947 Add the String() method to InputField 2023-03-13 14:55:10 +01:00
jordi fita mas c685fc496b Correctly format the scanned value of date and time InputFields 2023-03-13 14:54:28 +01:00
jordi fita mas c882158da3 Fix deletion of products with taxes on invoice edit 2023-03-13 14:32:02 +01:00
jordi fita mas dfdc9fde76 Restart tag sequence in edit_invoice test
Sometimes, depending on the order the tests are run, the edit_function
would try to insert a tag with a duplicate primary key, because the
sequence starts with 1 on an empty database.  So, make sure the next
sequence value is after the primary keys i have manually set.
2023-03-12 15:57:36 +01:00
jordi fita mas dab03e2b4f Change the color of the menu separator
I did not use the correct color according to the design.
2023-03-12 15:54:11 +01:00
jordi fita mas 0cd0fb1bb8 Add function to edit invoices 2023-03-11 20:58:20 +01:00
jordi fita mas 2bc05e948c Add invoice tags
I followed the same restrictions as Gitea’s topics, arbitrarily, because
if it is enough for repositories it should be for invoices too,
apparently.
2023-03-10 14:02:55 +01:00
jordi fita mas 5dedaefc22 Add button to download many invoices as PDF in a ZIP archive 2023-03-09 12:11:53 +01:00
jordi fita mas f3b841473f Add the context menu with the duplicate option
As per the design document.
2023-03-08 11:54:06 +01:00
jordi fita mas 0c8edb9cae Add option to duplicate an invoice
With Oriol we agreed that a duplicate is just the new invoice form
prefilled with the data from another invoices, but without the number or
the date.
2023-03-08 11:26:02 +01:00
jordi fita mas d725dcf529 Add missing call to PaymentMethod.FillValue 2023-03-08 11:19:59 +01:00
jordi fita mas 3dd0b0136e Adjust the design of invoice status menu to the one made by Oriol 2023-03-08 10:49:03 +01:00
jordi fita mas fab9e2c278 Use add_product function in demo to add products
This is what the application does, so it serves a documentation on how
to use the SQL API too.
2023-03-07 12:41:12 +01:00
jordi fita mas 625617cfbc Set different invoice statuses in the demo 2023-03-07 12:35:21 +01:00
jordi fita mas 039bf3abbd Add the “menu” to change invoice statuses 2023-03-07 11:52:09 +01:00
jordi fita mas d8997de654 Add invoices to the demo data 2023-03-06 11:23:11 +01:00
jordi fita mas f77f933e4a Add the payment method to invoices 2023-03-05 18:50:57 +01:00
jordi fita mas 93e92cf62d Move call to mustGetTaxOptions of AddProducts
I was trying to query the database while the connection was still busy
quering the products.
2023-03-05 18:43:22 +01:00
jordi fita mas 31ef3ea47a Add company’s default payment method
I had to use a deferrable foreign key because the payment methods have
a reference to the company, and the company now a circular reference to
payment method.
2023-03-04 22:15:52 +01:00
jordi fita mas 9894925742 Add the payment method relation and corresponding form 2023-03-03 16:49:06 +01:00
jordi fita mas b84f1774f9 Replace static legal disclaimer with a database field 2023-03-02 10:24:44 +01:00
jordi fita mas d6034ad732 Add discount and tax classes columns to invoice
This was actually the (first) reason we added the tax classes: to show
them in columns on the invoice—without the class we would need a column
for each tax rate, even though they are the same tax.

The invoice design has the product total with taxes at the last column,
above the tax base, that i am not so sure about, but it seems that it
has not brought any problem whatsoever so far, so it remains as is.

Had to reduce the invoice’s font size to give more space to the table
or the columns would be right next to each other.  Oriol also told me
to add more vertical spacing to the table’s footer.
2023-03-01 14:08:12 +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 6a8ebab686 Add “bottom margin” to the invoice’s legal text
I did not see it in the original design.
2023-03-01 11:43:06 +01:00
jordi fita mas 79ea2f366a Add grouping for form’s select field
We will only allow to select a tax from each of the tax classes, but
the user needs to know what class each tax belongs to, and grouping
the taxes by class in the select helps with that.
2023-03-01 11:40:23 +01:00
jordi fita mas 2add9c74c1 Fix typo in demo data 2023-03-01 11:12:56 +01:00
jordi fita mas 11d51df7fa Introduce the concept of tax class
We want to show the percentage of the tax as columns in the invoice,
but until now it was not possible to have a single VAT column when
products have different VAT (e.g., 4 % and 10 %), because, as far
as the application is concerned, these where ”different taxes”.  We
also think it would be hard later on to compute the tax due to the
government.

So, tax classes is just a taxonomy to be able to have different names
and rates for the same type of tax, mostly VAT and retention in our
case.
2023-02-28 12:02:27 +01:00