Commit Graph

32 Commits

Author SHA1 Message Date
jordi fita mas f1534e6cd2 Add the date of the last payment/collection to ODS export
Requested by Clara, that she wanted to know that date for internal
processes.  We agreed on adding only the most recent payment/collection
date, instead of adding all of them, for multiple payments/collections,
and she can know whether that date is for a partial or a complete
payment/collection with the status column.
2024-10-03 14:06:15 +02:00
jordi fita mas f5a9e819eb Allow to delete expenses
I remove the related taxes and attachments, but keep related payments
because i believe it is not likely that deleting a paid expense is what
the user wants.  If the user wants to do so, she can delete the payments
first.

Part of #84
2024-09-09 00:08:02 +02:00
jordi fita mas 0b74c7a91c Instruct htmx that HTTP 422 is not a “fatal error”
I use HTTP 422 to signal that a form was submitted with bad data,
which i believe is the correct status code: “indicates that the server
understands the content type of the request content […], and the syntax
of the request content is correct, but it was unable to process the
contained instructions.”[0]

htmx, however, treats all 4xx status codes as error and, by default,
does not swap the target with the response’s content.  Until i found out
that i could change that behaviour, i worked around this limitation by
returning HTTP 200 for htmx requests, but it is a waste of time given
that htmx _can_ accept HTTP 422 as a non-error.

[0]: https://www.rfc-editor.org/rfc/rfc9110#name-422-unprocessable-content
2024-08-27 11:07:39 +02:00
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 ac0143b2b0 Remove the status parameter from add_expense and edit_expense, and forms
Users are no longer expected to manually set the status of an expense
and, instead, have to add payments to such expense to mark it as partial
or paid.

That means that the PL/pgSQL functions must not accept a status
parameter, the edit and new forms should no longer have a field for
the status, and that the expense list should no longer have the “quick
edit” for their status.  That’s why it no longer should have a pointer
cursor, unlike invoice or quote status.
2024-08-13 02:34:21 +02:00
jordi fita mas 58cef8c00b Refactor common code to download invoice and expenses attachments 2024-08-12 00:07:30 +02:00
jordi fita mas f546632a89 Remove a stray Println from expenseForm.MustFillFromDatabase 2024-08-07 00:47:34 +02:00
jordi fita mas c3fa23727f Include customer’s VAT number to the expense list in ODS too
It was requested by Clara.
2024-07-20 22:52:23 +02:00
jordi fita mas 65413637ac Add a column for each tax type when exporting invoices and expenses
In the HTML tables i only compute the aggregated amount by tax class
(e.g., IVA, IRPF), but here we need the actual tax (e.g., IVA 4 %)
because this spreadsheet is intended for accountants.

I can easily extract the amounts from invoice_tax_amount and
expense_tax_amount, but i also need to add the columns to the
spreadsheet, and always with the same order—does not matter much which,
only the same—, that’s why i had to sort the tax IDs when exporting, as
Go does not guarantee an order for maps.

Closes #92
2024-01-26 02:30:11 +01:00
jordi fita mas 4e831d94db Avoid panic error when there is no expense to compute the sum of 2023-11-06 13:18:02 +01:00
jordi fita mas 0fd0cf5a38 Add the sum of the base and taxes to expenses’ index
Expands on #79
2023-10-02 16:36:42 +02:00
jordi fita mas 80a6a802a2 Make sure the selected taxes in show expense is nil if there is none
For some reason, pgx tries to convert [""] to an int array and fails,
because "" is not a number, of course.
2023-10-02 12:49:54 +02:00
jordi fita mas 831becf6fd Add the base and tax columns to expenses’ index
Closes #80
2023-10-02 12:16:50 +02:00
jordi fita mas 60ec335769 Sort expenses by date desc, and then by name and total
This make more sense, as is the same order user by invoices, and the
most recent expense is at the top.

Closes #79
2023-10-02 11:04:35 +02:00
jordi fita mas 0c4ef97dff Add option to export the list of quotes, invoices, and expenses to ODS
This was requested by a potential user, as they want to be able to do
whatever they want to do to these lists with a spreadsheet.

In fact, they requested to be able to export to CSV, but, as always,
using CSV is a minefield because of Microsoft: since their Excel product
is fucking unable to write and read CSV from different locales, even if
using the same exact Excel product, i can not also create a CSV file
that is guaranteed to work on all locales.  If i used the non-standard
sep=; thing to tell Excel that it is a fucking stupid application, then
proper applications would show that line as a row, which is the correct
albeit undesirable behaviour.

The solution is to use a spreadsheet file format that does not have this
issue.  As far as I know, by default Excel is able to read XLSX and ODS
files, but i refuse to use the artificially complex, not the actually
used in Excel, and lobbied standard that Microsoft somehow convinced ISO
to publish, as i am using a different format because of the mess they
made, and i do not want to bend over in front of them, so ODS it is.

ODS is neither an elegant or good format by any means, but at least i
can write them using simple strings, because there is no ODS library
in Debian and i am not going to write yet another DEB package for an
overengineered package to write a simple table—all i want is to say
“here are these n columns, and these m columns; have a good day!”.

Part of #51.
2023-07-18 13:29:36 +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 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 7d55e949fc Validate expenseForm.Text only once 2023-07-13 18:14:06 +02:00
jordi fita mas b48a974086 Add expenses statuses
We only want two statuses for expense: not yet paid (pending), and paid.
Thus, it is a bit different from quotes and invoices, because expenses
do not pass throw the “workflow” of created→sent→{pending,paid}. That’s
way in this case the status field is already in the new expense form,
instead of hidden, and by pending is not equivalent to created but
unpaid (i.e., the same status color).

With the new select field in the form, the file field no longer can
span two columns or it would be alone on the next row.

Closes #67.
2023-07-11 15:33:26 +02:00
jordi fita mas a068784a22 Remove unused company parameter from mustCollectExpenseEntries
The company is now in the filters form and there is no need for that
company parameter.
2023-07-02 20:06:45 +02:00
jordi fita mas 1c0f126c58 Split contact relation into tax_details, phone, web, and email
We need to have contacts with just a name: we need to assign
freelancer’s quote as expense linked the government, but of course we
do not have a phone or email for that “contact”, much less a VATIN or
other tax details.

It is also interesting for other expenses-only contacts to not have to
input all tax details, as we may not need to invoice then, thus are
useless for us, but sometimes it might be interesting to have them,
“just in case”.

Of course, i did not want to make nullable any of the tax details
required to generate an invoice, otherwise we could allow illegal
invoices.  Therefore, that data had to go in a different relation,
and invoice’s foreign key update to point to that relation, not just
customer, or we would again be able to create invalid invoices.

We replaced the contact’s trade name with just name, because we do not
need _three_ names for a contact, but we _do_ need two: the one we use
to refer to them and the business name for tax purposes.

The new contact_phone, contact_web, and contact_email relations could be
simply a nullable field, but i did not see the point, since there are
not that many instances where i need any of this data.

Now company.taxDetailsForm is no longer “the same as contactForm with
some extra fields”, because i have to add a check whether the user needs
to invoice the contact, to check that the required values are there.

I have an additional problem with the contact form when not using
JavaScript: i must set the required field to all tax details fields to
avoid the “(optional)” suffix, and because they _are_ required when
that checkbox is enabled, but i can not set them optional when the check
is unchecked.  My solution for now is to ignore the form validation,
and later i will add some JavaScript that adds the validation again,
so it will work in all cases.
2023-06-30 21:32:48 +02:00
jordi fita mas 07c1071975 Add total amount for quotes, invoices, and expenses tables
We have shown the application to a potential user, and they told us that
it would be very useful to have a total in the table’s footer, so that
they can verify the amount with the bank’s extracts.
2023-06-20 11:33:28 +02:00
jordi fita mas 8a4f80783d Rename Customer expense filter to Contact
It would be very unusual to have an expense from a customer, and we do
not have (yet) a name for supplier or whatever it should be here, so i
used the same name we use for the column in the table.
2023-06-20 11:17:07 +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 7921b9cf80 Add attach_to_expense SQL function
Just to avoid SQL “logic” in Go source.
2023-05-15 12:38:40 +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 f0f98e200c Add inline tag form for expenses 2023-05-08 12:58:54 +02:00
jordi fita mas 664088c748 Add filter form to expenses 2023-05-07 22:49:52 +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