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.
It is common to want to enumerate in a description, for instance when
adding specifications for a hosting, and that enumeration should be
formatted as the user wrote, otherwise it becomes useless.
Closes#94.
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
This is mostly to be able to sqitch rebase and restart with the demo
data during development, as it is very unlikely that i will need to
revert those at this point.
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
If there were contacts added with add_contact passing an empty string
to `web` parameter, contact_web would not have such row, and during the
revert the `web` parameter of contact would end up being NULL.
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.
I mistakenly thought that <small> was de inverse of the deprecated <big>
element, but apparently it is for small-print text and such, thus suited
for this case.
This is mostly to reassure people that we are running the same version
as published on numerus.cat. Or at least, try.
Go 1.18 adds the info from git if the package is build from a git
repository, but this is not the case in OBS, so i instead relay on a
constant for the version number. This constant is “updated” by Debian’s
rules, mostly due to the discussion in [0].
[0]: https://github.com/golang/go/issues/22706
There are no sales yet in Numerus, and having that summary there
confuses people.
He left the space there, without any text or background color, to
maintain the position of the rest.
Closes#87.
This is mainly to be able to stylize them using CSS; the current style
i set i just a placeholder to check that it works as expected.
Most of these links needs to check for the URI’s prefix, because they
are links to a whole section, but the first link must check for the
exact match, otherwise it would match every other URI, as all of them
start with /company/{uuid}.
The server does not return the markup for the top navigation when usin
HTMx, though, hence i have to change the current class using JavaScript.
I am not sure if the correct value for aria-current is “page” when the
link is not for the actual page the user is currently in, like when is
in the new quote page, but it seems to be the most appropriate value
from the enumeration given in the specifications, except, perhaps, for
the “location” value, but i was unable to find any example of that value
anywhere.
Part of #89.
I use customized built-in components, extended from any HTML elements
(e.g., HTMLDivElement), for product search, multiselect, and tags input
fields. The idea is that people that without JavaScript could still use
the regular, non-customized, inputs.
It turns out, this does not work so well: the CSS assumes that
JavaScript is enabled, and web components supported by the browser. If
one of these fails, then the controls are unusable—the multiselect is
too short, and the tags field accepts invalid characters that the
backend does not validate until it fails with a database error.
We discovered this because Apple does not implement customized built-in
components[0], hence it does not use my JavaScript code and forms,
the expenses forms in particular, are almost useless.
The way to fix this is to replace the regular inputs with autonomous
web components, extended from HTMLElement, using JavaScript, because
it would only do so with JavaScript enabled, and the CSS style would
only apply to these components, not the regular input fields.
However, currently i do not have time to do the proper fix, and have
to use a polyfill for Safari to support customized built-in components.
Shame.
[0]: https://lists.w3.org/Archives/Public/public-webapps/2013OctDec/0801.html
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….
It makes no sense to retrieve the same OIDs each and every connection,
because they are not going to change unless the database is reset,
something it is very unlikely to happen in production.
Thus, it is best to query them the first time the application connects
to the database, that it is done at startup to query the available
languages, and then reuse the OIDs.
I can get away of using an “unprotected” map, instead of sync.Map or a
map in tandem with sync.RWMutex, because the application establishes a
connection at startup from a single goroutine, and it registers _all_
types we will need to register within the application’s lifespan, hence
it there will be no more writes to that map once the web server is
listening for incoming connections.
This is risky, however, and i hope i do not have to regret it.
The problem is that parse_price('', 2) returned NULL instead of throwing
and exception: it seems that accessing var[1] of a text[] variable set
to the empty array, {}, returns NULL, and NULL::integer is, of course,
still NULL.
Apparently, this is the only case, until now, that i had an empty
subtotal, and i did not know what to do: should i keep the function as
is and just handle its NULL return, change it to return 0 in that case,
or raise an exception?
The argument for the first two options, to leave it as is or to
return zero, was that it was convenient for me to allow empty strings as
input values, because that’s what i get from an empty <input>; returning
zero would avoid an extra coalesce everywhere the function was used.
The argument in favor to the last option, an exception, was that the
empty string does not represent an integer, nor a “unknown” (NULL)
integer, therefore the function should do the same when i pass in any
other string that does not represent an integer, just as “a.b”.
At the end i went for option three, because it is the one that breaks
fewer expectatives: casting an empty string to integer, or passing
an empty string as the first value to to_number() throw and exception in
PostgreSQL; my function should do the same. Heck, that what **i**
expected it to do because of the casting inside the function.
To still allow empty strings as parameter to compute_new_expense_amount,
the only case so far, i only had to check for that empty string and
convert it to the string representation of zero, so that parse_price
returns the value i want for that function. This, of course, breaks
the same expectatives as returning NULL for to_price, but i think it is
OK in this case because to_price is more general—used in many more
cases—than compute_new_expense_amount, which is only intended for that
HTML form.
Closes#77.
When adding “free-form products” to quotes they do not have a product
ID, but i has coalescing the NULL to zero because product_id is an
integer and can not coalesce a nullable integer to an empty string.
However, that causes problems when trying to create the invoice for that
quote, because it tries to add products that have an ID of 0 and the
foreign key, obviously, fail.
At first i modified NewInvoiceProductArray.EncodeBinary to check for
"0" as well as the empty string, but i realized this was wrong: the
problem was because i gave these products an ID when they do not have
any. And the solution is to cast product_id to a text, which is what
will get converted anyway because i the only thing i do to it is to
store to a string-backed InputForm field.
Closes#73.