Commit Graph

335 Commits

Author SHA1 Message Date
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 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 f917ce84dd Replace call to deprecated ioutil.ReadAll with io.ReadAll
Starting from Go 1.16, ioutil.ReadAll simply calls io.ReadAll.
2023-07-02 20:04:45 +02:00
jordi fita mas eb845edf0a Change menu’s and profile icon
Eventually, we will allow people to upload their own profile images,
but until then we’ll use a “generic” profile icon instead of the closed
icon, that means nothing to users.

We wanted to have the same icon for that menu than for the Account item,
but the one we used until now did not look inside the round button for
the menu, thus we changed the two.

Related to #55.
2023-07-02 19:49:03 +02:00
jordi fita mas 2299ec9f8c Add empty IBAN and BIC to demo contacts 2023-07-02 02:26:35 +02:00
jordi fita mas 20827b2cfb Add IBAN and BIC fields to contacts
These two fields are just for information purposes, as Numerus does not
have any way to wire transfer using these, but people might want to keep
these in the contact’s info as a convenience.

Since not every contact should have an IBAN, e.g., customers, and inside
SEPA (European Union and some more countries) the BIC is not required,
they are in two different relations in order to be optional without
using NULL.

For the IBAN i found an already made PostgreSQL module, but for BIC i
had to write a regular expression based on the information i gathered
from Wikipedia, because the ISO standard is not free.

These two parameters for the add_contact and edit_contact functions are
TEXT because i realized that these functions are intended to be used
from the web application, that only deals with texts, so the
ValueOrNil() function was unnecessarily complex and PostreSQL’s
functions were better suited to “convert” from TEXT to IBAN or BIC.
The same is true for EMAIL and URI domains, so i changed their parameter
types to TEXT too.

Closes #54.
2023-07-02 02:08: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 30cd15ee89 Change IDs of demo SQL script to prevent coincidences
This is to avoid the problem that if i mistype `company_id = 1` instead
of `company_id = $1`, like i fixed in ee0b5d0b, at least it won’t work
in testing now, as all IDs have a three digits number.
2023-06-21 10:02:06 +02:00
jordi fita mas f40e4fdb2e Fix passing company ID to expenses chart query
By mistake, i was using 1 instead of $1, but i all was OK in testing
because there is only a single company with ID = 1.
2023-06-21 09:47:23 +02:00
jordi fita mas ee0b5d0bdc Rename Contact to Customer in quotes and invoices’ fields
In this case, the invoicee or quotee _is_ a (potential) customer, so
there is no point on calling them “contact”.
2023-06-20 11:37:02 +02:00
jordi fita mas de2a2f5912 Updated contacts’ table heading to read Contact instead of Customer 2023-06-20 11:34:00 +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 1ad771b771 Update module dependencies to match the version of Debian 12 packages 2023-06-17 20:42:23 +02:00
jordi fita mas 055e92fb23 Internationalize and localize the home template
Had to add an `unsafe` function to be able to translate text with HTML
fragments in it, although the fragments are added back with printf
because the login link is actually not translatable.
2023-06-16 10:58:40 +02:00
oriol carbonell pujolàs 826741a381 Primer pas al frontal de visitants 2023-06-16 10:17:58 +02:00
jordi fita mas 3af40cc7bc Update weasyprint parameters for version 57.2
Debian 12 (bookworm) has upgraded its weasyprint version and it no
longer includes the --format parameter, because now it only can output
to PDF.
2023-06-15 23:16:53 +02:00
jordi fita mas 010e174de7 Change CURRENT_TIMESTAMP parameter for col_default_is
It turns out i have been **years** doing this wrong: you are supposed to
pass that value as a text, like 'CURRENT_TIMESTAMP', not like the
keyword so that it returns the current timestamp as a timestamptz.

However, i have been doing it wrong because of a bug in previous
versions of pgTAP[0], that did not take into account keywords such as
CURRENT_TIMESTAMP or CURRENT_DATE and was comparing their actual values,
not the names, therefore i thought that i misread the documentation.

Only now have discovered this because Debian 12 upgraded pgTAP version
to 1.2.0.

[0]: https://github.com/theory/pgtap/issues/244
2023-06-15 14:21:09 +02:00
jordi fita mas 73682462da debian: Update PostgreSQL version to 15, used in Debian 12 2023-06-15 12:19:08 +02:00
jordi fita mas 6732d654a4 Reduce the amount of useless data in request’s log
I tried to have a log line that uses the “common” format from Apache,
because i thought that it would help me reuse regexps i have defined for
fail2ban filters and such.

However, it makes no much sense.

For once, i was repeating the date and time: log.Printf already does
that for me.

And, second, i do not need that data in Numerus’ log because i always
run it behind a proxy that _has_ a “common”-formatted log file, so
there is no need for me to repeat all that data again.

What i need is the IP, to know whether remotedAdd() function works as
expected; the method, to check that the override does its job; the path,
to know what resource the browser requested; the response status code,
so that i do not need to open the browser console for that; the response
size, to keep on eye that i do not return a lot of data; and the
total response time, to realize how long my unoptimized SQL queries
slows the application down.

The rest, Apache should do its job and record it in its log file for
fail2ban and whatever i need the logs for in the future.
2023-06-13 16:05:40 +02:00
jordi fita mas eb207a01fc Log requests to stdout
This is so that i can have two output files: the one with accesses
(stdout) and the one with errors (stderr).
2023-06-13 15:08:23 +02:00
jordi fita mas 3c14447ef9 Debian: add service and post installation script to create user and group 2023-06-13 14:48:43 +02:00
jordi fita mas d79ddc6731 Log the remote address, and only trust localhost proxies
I need the actual remote address to add fail2ban rules for it, but i
also to not want everyone to be able to fake X-Forward-For HTTP headers.
Which can contain multiple ip addresses, by the way, so i have to get
only the first one, as the others will be the proxies that the request
has been (re)forwarded to.
2023-06-13 14:21:54 +02:00
jordi fita mas ac28393398 Tag sqitch.plan with first version 2023-06-12 16:05:50 +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 2d5a644c9d Add the “invoiced” quote status
This is for people to mark quotes that are already invoiced and filter
them out in the list.
2023-06-11 22:19:43 +02:00
jordi fita mas a16f696be5 Allow to create an invoice from the data of a quotation 2023-06-10 20:46:03 +02:00
jordi fita mas f43949dd43 Add quote number formatting and next number field to tax details
The same as for invoices: to allow people to have their own numbering
scheme, and for these that start using the program in the middle of the
current year.
2023-06-09 12:43:50 +02:00
jordi fita mas 6c3a3ff232 Allow empty contact and payment method for quotes
I have to use a value to be used as “none” for payment method and
contact.  In PL/pgSQL add_quote and edit_quote functions, that value is
NULL, while in forms it is the empty string.  I can not simply pass the
empty string for either of these fields because PL/pgSQL expects
(nullable) integers, and "" is not a valid integer and is not NULL
either.  A conversion is necessary.

Apparently, Go’s nil is not a valid representation for SQL’s NULL with
pgx, and had to use sql.NullString instead.

I also needed to coalesce contact’s VATIN and phone, because null values
can not be scanned to *string.  I did not do that before because
`coalesce(vatin, '')` throws an error that '' is not a valid VATIN and
just left as is, wrongly expecting that pgx would do the job of leaving
the string blank for me.  It does not.

Lastly, i can not blindly write Quotee’s tax details in the quote’s view
page, or we would see the (), characters for the empty address info.
2023-06-08 13:05:41 +02:00
jordi fita mas 5537a53834 Add margin between quotee and terms and conditions 2023-06-08 12:55:12 +02:00
jordi fita mas 9bb5bcd820 Use the same style for quoter and quotee than invoicer and invoicee 2023-06-08 12:52:40 +02:00
jordi fita mas 9fab65f108 Add terms and conditions to invoice’s view 2023-06-08 12:52:10 +02:00
jordi fita mas ba6f51ac5d Fix getting quote’s terms and conditions from form and database 2023-06-08 12:50:16 +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 efbb4da07f Added SQL views to compute computations amounts and edit them 2023-06-07 15:31:20 +02:00
jordi fita mas f54681de93 Require invoice_product_product for edit_invoice 2023-06-07 15:09:35 +02:00
jordi fita mas 86bf8765fc Use the correct integer literal for invoice_amount
PostgreSQL actually already casts the strings to integers, but best if
everything is as it should.
2023-06-07 14:54:29 +02:00
jordi fita mas a066726c2e Add function to create new quotes
I had to add the quote_number_format to company, similar to how we do
with invoices.
2023-06-07 14:14:48 +02:00
jordi fita mas aeca90256c Remove setting custom number invoice format from add_expense test
It is not necessary for this test and, since the column already has a
default value, setting it there seems like it might have any
consequence.
2023-06-07 13:27:49 +02:00
jordi fita mas 0e20eab46a Add test for invoice_number_counter counter_zero_or_positive constraint 2023-06-07 13:19:06 +02:00
jordi fita mas 775cdef097 Add foreign key constraint to invoice_number_counter.company_id 2023-06-07 13:11:29 +02:00
jordi fita mas 35b12f7ea4 Add relations for sales quotations and their products
They are mostly the same as invoices, but the contact and payment method
are optional, thus, like other optionals fields, i created relations to
link these that have payment method or contact, to avoid NULL columns in
quote.

Still missing functions to add and edit quotations, and views to compute
their tax and total amount.
2023-06-06 21:08:31 +02:00
jordi fita mas d7a256804f Move available_language.sql before user.sql in sqitch.plan
This is because when i create the demo users then i can no remove the
available languages before users, due the constrain, and i can no use
sqitch rebase or revert.
2023-06-06 19:09:48 +02:00
jordi fita mas 0ed0edeff6 Specify build_cookie dependency for login function 2023-06-04 22:59:25 +02:00
jordi fita mas 083d14e324 Allow to change the current year’s invoice number counter
This is for new users that do not start using the application from the
beginning of the current fiscal year and, therefore, need to create
invoices starting from a specific number.

I had to change the constraint on the currval to allow zero, otherwise
it would not be possible to set 1 as the next number, because users
can also not delete the row.
2023-05-31 20:01:00 +02:00
jordi fita mas 1855122d16 Update translations 2023-05-29 00:02:55 +02:00
jordi fita mas 8529da1615 Use HTMx to delete and restore invoice products
It is better that way because it works without JavaScript; if HTMx is
not available, it will just use regulars forms.

The problem is that most of the submit buttons where using formaction
to send the request to a different action, and only one button was the
“real” action.  Since i could not pass the formaction to
invoice-product-form template, i have changed the “default” action to
the one with “ancillary” functions.

I have to use a different action to remove for each product because i
can not pass the index to the backend without JavaScript: it only
depends on the button click, that already has a name for the action.
Thus, in a way, i have “merged” the action and the index in a single
name.
2023-05-29 00:01:11 +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 19f81128ec Keep the invoice number when requesting an update while editing 2023-05-26 14:02:39 +02:00