Commit Graph

58 Commits

Author SHA1 Message Date
jordi fita mas 292720de28 Add an edit form for payment methods
I do not particularly enjoy an htmx-only way of doing that, because it
means that it can only work with JavaScript, but i think this is already
a lost cause, unfortunately.  If i have time, i will try to make the
HTML-only form work too.

In this case, i have to put back the same row when updating or
cancelling the form, which is inside index.html.  Instead of moving that
part to a separate file, i tried to define a block as a “template
fragment” and try to render that part only.  Surprisingly, it works;
i am happy.

Closes #74.
2024-08-30 02:45:40 +02:00
jordi fita mas e8a44e480e Add field to update default payment method
It is only shown if there is more than one payment method, otherwise
it seems useless as there are nothing to change.

This closes #72.
2024-08-28 11:10:03 +02:00
jordi fita mas d4ef6c3254 Add a new collection “subsection” for invoices
This is mostly the same subsection as payments is for expense, added in
4f646e35d.  In this case i call it “collections”, but it is actually
the same payments section.
2024-08-21 11:22:53 +02:00
jordi fita mas 4f646e35d6 Add a new payments “subsection” for expenses
With Oriol we agreed that to add new payments to expenses we should
direct users to a separate payments section, much like the general
payments but centered around the payments of the given expense.

In fact, the only thing i had to do is extract the expense from the
URL, and then adjust the base URI to keep things always within the
correct section; the rest of the code is shared with the general
section.
2024-08-17 05:31:01 +02:00
jordi fita mas a30e015639 Add inline tags form to payments 2024-08-15 04:18:35 +02:00
jordi fita mas f95936c523 Split the tax details “mega dialog” into separate pages
I needed to place the payment accounts section somewhere, and the most
logical place seemed to be that dialog, where users can set up company
parameters.

However, that dialog was already saturated with related, but ultimately
independent forms, and adding the account section would make things
even worse, specially given that we need to be able to edit those
accounts in a separate page.

We agreed to separate that dialog into tabs, which means separate pages.
When i had everything in a separated page, then i did not know how to
actually share the code for the tabs, and decided that, for now, these
“tabs” would be items from the profile menu.  Same function, different
presentation.
2024-08-14 04:08:13 +02:00
jordi fita mas c95f172499 Add attachments to payments 2024-08-12 00:08:18 +02:00
jordi fita mas 778f9c1555 Allow removal of payments
I am using an htmx-infused button to remove the payment, but that
button can not have the CSRF token as value, thus i have to send it in a
header.

The removal of payments warrants a functions, instead of just DELETE
(and CASCADE) as i do for payment methods, because i have to adjust the
status of expenses too.  Since i already have functions for everything,
it is not worth using triggers just for that.
2024-08-11 03:22:37 +02:00
jordi fita mas ad5bc271b6 Add the payments section
This actually should be the “payments and receivables” section, however
this is quite a mouthful; a “receivable” is a payment made **to** you,
therefore “payments” is ok.

In fact, there is still no receivables in there, as they should be in
a separate relation, to constraint them to invoices instead of expenses.
It will be done in a separate commit.

Since this section will be, in a sense, sort of simplified accounting,
i needed to introduce the “payment account” concept.  There is no way,
yet, for users to add them, because i have to revamp the “tax details”
section, but this commit started to grow too big already.

The same reasoning for the attachment payment slips as PDF to payment:
something i have to add, but not yet in this commit.
2024-08-10 04:34:07 +02:00
jordi fita mas 5f7b798eb4 Prefill login form when using the demo database
This is to help up “sell” the service: people can look around the demo
to see whether it fits them.  Of course, everyone should have the same
username in the demo.

We talked about having the username and password displayed above the
form in the template, but i think it makes more sense to give users as
little work as necessary.  Plus, that means i do not have to write them
down while developing.

Whether the database is demo or not is not something that directly
depends on the environment, but rather on which database we are
connected to, thus an environment variable would not make much sense—it
has to be something of the database.

PostgreSQL has no PRAGMA application_id or PRAGMA user_version as with
SQLite to include application-specific values to the database.  The
equivalent would be customized options[0], intended for modules
configuration, but that would require me to execute an ALTER DATABASE
in demo.sql with an specific datbase name, or force the use of psql to
run script the script, because then i can use the :DBNAME placeholder.

I guess that the most “standard” way is to just create a function that
returns a know value if the database is demo.  Sqitch does not add that
function, therefore it is unlikely to be there by change unless it is
the demo database.

https://www.postgresql.org/docs/15/runtime-config-custom.html
2024-01-20 20:23:26 +01:00
jordi fita mas e0bdb89472 Add legal disclaimer and privacy and cookies policies’ texts
The legal stuff. Required by Spanish law when setting up a site intended
for pecuniary gain, directly or indirectly.

Now we have more pages to the “public web”, and moved the header and
footer from home to the common layout.  I also took the opportunity to
change the element from <div> to the appropriate element based on their
use (i.e., <header> and <footer>).

I removed the <div> around the logo because i did not see any use for
it.  I may be from a previous design iteration, but it had no style
applied nor any usage at all in JavaScript.
2024-01-19 23:05:01 +01:00
jordi fita mas 998159d1d7 Add option to switch to another company
This is for users that belong to more than one company.  It is just a
page with links to the home of each company that the user belongs to.

Had to add a second company to the demo data to test it properly, even
though i already have unit tests for multicompany, but, you know….
2023-11-06 13:52:34 +01:00
jordi fita mas a7c1df20f0 Compute the total amount, base plus taxes, of all expenses
This works mostly like invoices: i have to “update” the expense form
to compute its total based on the subtotal and the selected taxes,
although in this case i do no need to compute the subtotal because that
is given by the user.

Nevertheless, i added a new function to compute that total because it
was already hairy enough for the dashboard, that also needs to compute
the tota, not just the base, and i wanted to test that function.

There is no need for a custom input type for that function as it only
needs a couple of simple domains.   I have created the output type,
though, because otherwise i would need to have records or “reuse” any
other “amount” output type, which would be confusing.\

Part of #68.
2023-07-13 20:50:26 +02:00
jordi fita mas bb7af20a17 Add attachments to invoices
Works exactly the same as for expenses, and this is sometimes convenient
for keeping transfer slips from customers and such.

I actually did not know where to add the download from this attachment,
because if add a column to the index it can easily be confused with the
download icon for the actual invoice.

Part of #66.
2023-07-12 20:06:53 +02:00
jordi fita mas 183b8d3ed9 Allow importing contacts from Holded
This allows to import an Excel file exported from Holded, because it is
our own user case.  When we have more customers, we will give out an
Excel template file to fill out.

Why XLSX files instead of CSV, for instance? First, because this is the
output from Holded, but even then we would have more trouble with CSV
than with XLSX because of Microsoft: they royally fucked up
interoperability when decided that CSV files, the files that only other
applications or programmers see, should be “localized”, and use a comma
or a **semicolon** to separate a **comma** separated file depending on
the locale’s decimal separator.

This is ridiculous because it means that CSV files created with an Excel
in USA uses comma while the same Excel but with a French locale expects
the fields to be separated by semicolon.  And for no good reason,
either.

Since they fucked up so bad, decided to add a non-standard “meta” field
to specify the separator, writing a `sep=,` in the first line, but this
only works for reading, because saving the same file changes the
separator back to the locale-dependent character and removes the “meta”
field.

And since everyone expects to open spreadsheet with Excel, i can not
use CSV if i do not want a bunch of support tickets telling me that the
template is all in a single line.

I use an extremely old version of a xlsx reading library for golang[0]
because it is already available in Debian repositories, and the only
thing i want from it is to convert the convoluted XML file into a
string array.

Go is only responsible to read the file and dump its contents into a
temporary table, so that it can execute the PL/pgSQL function that will
actually move that data to the correct relations, much like add_contact
does but in batch.

In PostgreSQL version 16 they added a pg_input_is_valid function that
i would use to test whether input values really conform to domains,
but i will have to wait for Debian to pick up the new version.
Meanwhile, i use a couple of temporary functions, in lieu of nested
functions support in PostgreSQL.

Part of #45

[0]: https://github.com/tealeg/xlsx
2023-07-03 00:05:47 +02:00
jordi fita mas dde4395888 Add the most minimal home page design
This is so that Oriol can start working on it.
2023-06-11 22:24:25 +02:00
jordi fita mas 9931796744 Add HTTP controller and view to add quotes
It still does not support quotes without contact or payment.
2023-06-07 16:35:31 +02:00
jordi fita mas ce42880697 Begin the dashboard with expenses, gross income, net income, and taxes
For now i use a too-long SQL query for that, but will probably replace
it with a view.  I have to check that it is correct before i do so,
however.
2023-05-16 14:56:49 +02:00
jordi fita mas 3161d54aba Add expense’s file input to new and edit forms
I had to change MethodOverrider to check whether the form is encoded as
multipart/form-data or i would not be able to get the method field from
forms with files.

For now i add the file manually, i.e., outside add_expense and
edit_expense PL/pgSQL functions, because it was faster for me, but i
will probably add an attach_to_expense function, or something like that,
to avoid having the whole ON CONFLICT logic inside Golang—this belongs
to the database.
2023-05-14 18:46:16 +02:00
jordi fita mas f639602170 Add contact’s inline form for tags 2023-05-12 11:32:39 +02:00
jordi fita mas 856ddde00e Add the inline form for product tags 2023-05-09 12:18:31 +02:00
jordi fita mas f0f98e200c Add inline tag form for expenses 2023-05-08 12:58:54 +02:00
jordi fita mas 49c41681ce Add PUT method to expense’s URL and call edit_expense 2023-05-05 10:59:35 +02:00
jordi fita mas 55d650bd62 Add expenses’ index and add form 2023-05-03 12:46:25 +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 d20573aa99 Allow editing invoice tags inline from the index table
I use the same pattern as HTMx’s “Click to Edit” example[0], except that
my edit form is triggered by submit and by focus out of the tags input.

I could not, however, use the standard focus out event because it would
also trigger when removing a tag with the mouse, as for a moment the
remove button has the focus and the search input dispatches a bubbling
focusout.  I had to resort to a custom event for that, but i am not
happy with it.

The autofocus attribute seems to do nothing in this case, so i need to
manually change the focus to the new input with JavaScript.  However,
this means that i can not use the same input ID for all the forms
because getElementById would always return the first in document order,
changing the focus to that same element and automatically submit the
form due to focus out.  That’s why in this form i append the invoice’s
slug to the input’s ID.

Finally, this is the first time i am using an HTMx-only solution and i
needed a way to return back just the HTML for the <td>, without <title>,
breadcrumbs, or <dialog>.  In principle, the template would be the
“layout”, but then i would need to modify everything to check whether
the template file is empty, or something to that effect, so instead i
created a “standalone” template for these cases.

[0]: https://htmx.org/examples/click-to-edit/
2023-04-11 10:46:27 +02:00
jordi fita mas dbfa58699c Show the duplicate invoice form in a dialog
Had to add a new hidden field to the form to know whether, when the
request is HTMx-triggered, to refresh the page, as i do when duplicating
from the index, or redirect the client to the new invoice’s view page,
but only if i was duplicating from that same page, not the index.

Since i now have to target main when redirecting to the view page, so
i had to add a location structure with the required json fields and all
that, when “refreshing” i actually tell HTMx to open the index page
again, which seems faster, now that i am used to boosted links.
2023-04-04 14:39:55 +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 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 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 5dedaefc22 Add button to download many invoices as PDF in a ZIP archive 2023-03-09 12:11:53 +01:00
jordi fita mas 039bf3abbd Add the “menu” to change invoice statuses 2023-03-07 11:52:09 +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 0d4fb124b4 Keep all “new invoice actions” on the same /new URI 2023-02-27 13:13:28 +01:00
jordi fita mas 4d2379555e Convert invoices to PDF with WeasyPrint
Although it is possible to just print the invoice from the browser, many
people will not even try an assume that they can not create a PDF for
the invoice.

I thought of using Groff or TeX to create the PDF, but it would mean
maintaining two templates in two different systems (HTML and whatever i
would use), and would probably look very different, because i do not
know Groff or TeX that well.

I wish there was a way to tell the browser to print to PDF, and it can
be done, but only with the Chrome Protocol to a server-side running
Chrome instance.   This works, but i would need a Chrome running as a
daemon.

I also wrote a Qt application that uses QWebEngine to print the PDF,
much like wkhtmltopdf, but with support for more recent HTML and CSS
standards.  Unfortunately, Qt 6.4’s embedded Chromium does not follow
break-page-inside as well as WeasyPrint does.

To use WeasyPrint, at first i wanted to reach the same URL as the user,
passing the cookie to WeasyPrint so that i can access the same invoice
as the user, something that can be done with wkhtmltopdf, but WeasyPrint
does not have such option.  I did it with a custom Python script, but
then i need to package and install that script, that is not that much
work, but using the Debian-provided script is even less work, and less
likely to drift when WeasyPrint changes API.

Also, it is unnecessary to do a network round-trip from Go to Python
back to Go, because i can already write the invoice HTML as is to
WeasyPrint’s stdin.
2023-02-26 17:26:09 +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 5c15b9de20 Add the bare-bones form for invoices 2023-02-11 22:16:48 +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
jordi fita mas 2799fdb8db Add companyURI for Go code too, not just templates 2023-02-04 10:43:42 +01:00
jordi fita mas 7d17620f48 Add the edit contact page 2023-02-03 13:57:43 +01:00
jordi fita mas 1ab48cfcbc Replace default router with github.com/julienschmidt/httprouter
I would fuck up handling URL parameters and this router has per-method
handlers, that are easier to work with, in some cases.
2023-02-03 12:30:56 +01:00
jordi fita mas 80f14d5818 Use MustGetText to get the company’s slug 2023-02-03 10:51:48 +01:00
jordi fita mas 1a7b9f6bdd Rename extension of templates to .gohtml
Apparently, there are tools that only know how to use that extensions
when referring to Go templates.
2023-01-30 16:48:21 +01:00
jordi fita mas 5a199a3d8e Add the contact relation and a rough first form 2023-01-29 15:14:31 +01:00
jordi fita mas 1712a81dfc Move the /profile under the company router
This is not necessary per se, but it makes my life easier because that
way i know which company the user was when she went to its profile and
can “return” back in the menu and future nav items.
2023-01-29 15:13:47 +01:00
jordi fita mas 666935b54c Add the tax relation with very rough form and handler 2023-01-28 14:18:58 +01:00
jordi fita mas 0a58e2699e Use a select for company’s country field
At first we thought that a regular text field would do, because we were
afraid that a dropdown would be worse from the point of view of user
experience, but then we realized that we need the country code for VAT
and phone validation, and we can not expect users to input that, of
course.

I had to add the first “i18n table” to the database with the name of all
countries in both Catalan and Spanish and Catalan; English is the
default.  For now i think i do not need a view that would select the
name based on the locale of the current request, because currently i do
not plan on adding any other such table —the currency uses the code and
the symbol, thus no need for localization.

However, now i need the language tag from the locale in order to get the
correct translation, and gotext does not give me any way to access the
inner language.  Thus the need for our Locale type.
2023-01-27 21:30:14 +01:00
jordi fita mas 627841d4dd Add the company relation and read-only form to edit
I do not have more time to update the update to the company today, but i
believe this is already a good amount of work for a commit.

The company is going to be used for row level security, as users will
only have access to the data from companies they are granted access, by
virtue of being in the company_user relation.

I did not know how add a row level security policy to the company_user
because i needed the to select on the same relation and this is not
allowed, because it would create an infinite loop.

Had to add the vat, pg_libphonenumber, and uri extensions in order to
validate VAT identification numbers, phone numbers, and URIs,
repectively.  These libraries are not in Debian, but i created packages
for them all in https://dev.tandem.ws/tandem.
2023-01-24 21:46:07 +01:00
jordi fita mas c037f671f8 Refactor authenticated redirection to a common handler 2023-01-24 21:44:23 +01:00
jordi fita mas 5505fa41c3 Use “layouts” for the common HTML between pages
Had to call xgettext on Go source files because now the title comes from
there, as i assume i will have titles like "Invoice #INVxxxx" that have
to come from the database that the template does not know.
2023-01-22 21:41:50 +01:00