They are not functions because i need to join them with the main
invoice relation, and although possible is a bit more awkward with
functions.
The taxes have their own relation because i will need them grouped by
their name in the PDF, so it will probably be a select for that
relation.
When updating the product list, i forgot to change the index in the
product field’s names and, therefore, i created invoices with only the
products until the first gap.
I have moved everything into a different file, even though it is related
to “db”, because it was starting to get a bit ugly.
Apparently i was doing too much work and had to read the code to
understand what i was supposed to do, because pgtypes’ documentation,
as all other projects from the same author, is almost non-existent.
I needed to add binary encoding function for the new_invoice_product
type, and currently it is not correct: i hardcoded the OID values, but
they are going to be different on a different database.
I can not use a PostgreSQL sequence because invoices need to be gapless,
and sequences are designed to not rollback, for performance reasons. In
this case, the performance is secondary because the law does not care.
They are to complete the invoice, so it can be in an invalid date, but
we do not want to force people to finish all required inputs before they
can add products or update quantities, do we?
Now had to add the empty option label for customer in all cases, because
it could be empty, although that should be done regardless in case
someone has a browser that does not validate fields.
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.
I have seen that pgx has the CollectRows function to do the same job as
that function. I can not use CollectRows because it uses generics and
requires Go 1.18, but i have adopted the same nomenclature they use.
Apparently, url.Values.Has and math.MaxInt was added to Go 1.17,
but on Debian Bullseye there is only Go 1.16. I do not want to
install a new version of Go to the server unless there is an
overwhelming reason, and a couple of methods are not. Thus, now i use
Go 1.16 too on my development machine, to avoid situations like this.
I store again the product’s name, description, and prices, because they
are bound to change, but the invoice should remain the same always.
That makes me wonder if i should do the same for seller’s and buyer’s
data, but that should be a different commit.
I’ve added the discount_rate domain because then i can test it
independently of the invoice_product relation, moreover i am sure i will
need it for simplified invoices too.
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.
I was using explicit IDs because i need them to satisfy foreign key
constraints, and also to look them up within the file, but then i had
the problem that the sequences would be left at 1, preventing me to
add new contacts or products, for instance.
Now i use the sequence exactly how the application will (i.e., with
default values), but i have to reset them to 1 to make the ID stable
even when i make tests with pgTAP on the same database.
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.
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.
Our company is a kind-of contact, although it does not appear in the
contact section, thus i could embed the contact form inside the tax
details form to reuse all the common fields.
I implemented the Valuer and Scanner interfaces to InputField and
SelectField for better passing values between the database and Go. I
had a conflict with the Value name and renamed the struct member to Val.
I also had to change the attributes array to be of type
template.HTMLAttr or html/template would replace `form="newtax"`
attribute to `zgotmplz="newtax"` because it deems it “unsafe”. I do
not like having to use template.HTMLAttr when assigning values, but
i do not know what else i can do now.
I was worried that i was repeating the AddInputErrors function for each
form, because they were basically the same. I could create a Form type
and make all forms embed it, but i realized that with a separate
validator i would have cleaner validation functions and would not need
the Valid field in the form that i am using only for that method.
Similar to the profile form, the login form now parses and validates
itself, with the InputField structs that the templates expect.
I realized that i was doing more work than necessary when parsing fields
fro the profile form because i was repeating the operation and the field
name, so now it is a function of InputField.
This time i needed extra attributes for the login form. I am not sure
that the Go source code needs to know about HTML attributes, but it was
the easiest way to pass them to the template.