It does nothing but compute the total of a booking, much like it does
for guests. In fact, i use the same payment relations to do the exact
same computation, otherwise i am afraid i will make a mistake in the
ACSI or such, now or in future version; better if both are exactly the
same.
The idea is that once the user creates the booking, i will delete that
payment, because it makes no sense to keep it in this case; nobody is
going to pay for it.
Had to reuse the grid showing the bookings of campsites because
employees need to select one or more campsites to book, and need to see
which are available. In this case, i have to filter by campsite type
and use the arrival and departure dates to filter the months, now up to
the day, not just month.
Had to change max width of th and td in the grid to take into account
that now a month could have a single day, for instance, and the month
heading can not stretch the day or booking spans would not be in their
correct positions.
For that, i needed to access campsiteEntry, bookingEntry, and Month from
campsite package, but campsite imports campsite/types, and
campsite/types already imports booking for the BookingDates type. To
break the cycle, had to move all that to booking and use from campsite;
it is mostly unchanged, except for the granularity of dates up to days
instead of just months.
The design of this form calls for a different way of showing the totals,
because here employees have to see the amount next to the input with
the units, instead of having a footer with the table. I did not like
the idea of having to query the database for that, therefore i “lifter”
the payment draft into a struct that both public and admin forms use
to show they respective views of the cart.
This is actually only used for plots, but, of course, it means that
every booking now can potentially have many booked campsites, and have
to create a relation for it.
I now have a conundrum regarding stay dates: i need them to be in the
same table as the campsite_id, because constraints only work on a single
relation and without the dates i can not make sure that i am not
overbooking a given campsite; but, on the other hand, all campsites
under the same booking must be for the same dates.
Where does stay belong, then? In booking or booking_campsite? If in
booking then i can not have a constraint that most assuredly will bite
me in the back, but if in booking_campsite then each campsite could
potentially have different dates.
As far as i can see, i can not use a exclude constraint with <> for
dates in booking_campsite to ensure that all rows with the same
booking_id have the same stay (i.e., exclude those that have a different
stay for the same booking_id).
For now, the say is in **both** relations: in booking, because i need it
when it is a prebooking, at least, and in booking_campsite for the
aforementioned constraint requirements.
Will this come back and bite me? Yes, it will. But what can i do?
I need the campsite_id in booking to know what row to show the booking
at. Besides the need of knowing which actual campsite has been booked,
of course.
This field is nullable because we can not now it until an employee has
confirmed the booking; until that point we only know the campsite type
customer requested. I do not care much if the campsite_id is from a
different campsite_type, because maybe the customer requested the change
by phone or what have you, therefore the database can not be that
strict. It must have a value if the booking is confirmed.
It helps me if the arrival_date and departure_date is a single
daterange, because then i can use `&&` and other range operators to work
with these dates. For instance, i have to intersect it with the range
displayed on the screen in order to know which day i have to put it.
But then i have to know whether the booking begins and ends in the
display range, because i only have to show arrival and departure (i.e.,
the box half-way within the first or last boxes) on these days only.
Customer told us that they are used to a view of the booking status of
each campsite in the form of a grid: each campsite is a row, and each
day a column; bookings are show as boxes from the first day to the last
day on the corresponding campsite’s row.
I do not yet show the booking boxes, but at least now i have the grid
and date selector form in place.
In the form i would need a couple of input[type=month], but this is not
yet supported in Firefox and Safari. According to MDN, one common way
to bypass that problem is to have two fields, one for the month and the
other for the year; i just did that, but had to create a new input type
in the `form` package just for this.
It turns out that, **this time**, at least, the way to compute the
discount is not by “the more expensive”, but “the more expensive _in a
given group_”.
However, there are a couple of options, such as motorhome, that can be
in different groups but only must be used once.
Customer wants this because the booking is not automatically created,
thus it is possible to overbook. They want to accept the payment of
those that they can actually book.
Since this address is used to send notifications when a new payment is
received, is quite important not to use the same address as in
production, or there is a non-trivial chance of confusion.
I swear i believe sometime before we said that the number of dogs is not
important and should be used only as a boolean, but apparently it is
wrong: it should be number_dogs * cost_per_pet.
After months of keeping what does the ACSI checkbox mean, now customer
told us that we should add a discount based on a series of
arbitrary conditions that, and need to be done NOW!
There is no UI to edit the conditions due to lack of time.
I always intended to delete draft payments after some time. I follow
WooCommerce’s default times: 1 day for draft and 1 hour for pending. No
other reason than we are used to it.
I added a cron job, rather than a systemd timer, because i want email
notifications, and because i do not yet know how to add many service
files in a Debian package.
We have some clients that are on slow connections that have trouble
uploading large images due to timeout, and receive a “Guru Meditation”
from Varnish that it could not fetch.
Redsys are a bunch of … something: they **only** recognize HTTP 200
as success; HTTP 204 apparently is an error for they, and don’t get
me started in not understanding what to do with an HTP 301.
Let’s just give in, and relax.
This is specially for the departure date: Firefox triggers a change each
time the user writes something, but since the date may be outside the
allowed range while the user is typing, the form replaces the input with
the “corrected” one. The idiomorph thing is to keep the focus in the
“same” field, from the point of view of the user.
It still happens if one is slow enough, but i guess people that want to
write instead of picking the date are usually fast typist. Let’s hope.
This is required by law.
I do not know why i have this value and the tax amount in the database,
but the payment percent and the number of days are hardcoded. I guess i
am such an inconsistent mess.
Apparently, the bank has to validate the fucking thing on the actual
domain, because of reasons, so we have to replace the current web in
production with this version in test mode, meaning that users **will**
believe they have paid, when in fact they have not.
The warning is for these few people that actually read such notices.
This is the mode they want to work with, but i could not test it because
they do not have it enabled in Redsys. For now, just add the status and
the code to handle the responses.
Now i store all responses, if they are for a valid payment, just in case
i fucked something up. I also needed it because an authorization hold
needs at least two responses: one to accept the hold, and another for
the settlement.
Had to do a couple of changes to the database: add the currency_code to
the payment relation, to format the price according to the payment’s
currency instead of the company’s; and the reference SQL function, to
replace the equivalent golang function, so that i can use it to index
payments.
The rest is mostly the same as any other page, except that the
individual payment’s page is not a form, but a regular info dump.
I also moved the payment settings as a sub-route of payments, as i
believe this makes more sense than an additional user menu item.
Customer wants to require a down payment of 30 % for bookings made
one week or more before the actual date, and to make the full payment
otherwise.
This would require yet another relation to keep these values. Fuck it;
i added them to the function, as they are very unlikely to change.
That forced me to change the test for draft_payment to use relative
dates, otherwise there is no way i can have stable results in the
future.
This is easier to read and requires less unit tests, but i only used
them in the new relations and fields for HEAD, because i do not see any
point on creating migrations just for that.
This can happen when the customer reaches the payment page, but then
returns back to the booking form via the back button: the browser
remembers the URI with the cart slug, trying to make it ready, and then
it fails because it is already pending.
I did not like the idea of modifying a payment that is already not
a draft, because it seems to me that can lead to errors if we receive
Redsys notifications of payments that are being changed back to draft.
In fact, i believe that draft payments maybe should go to a different
relation altogether, so that i can prevent UPDATE on payment by guests,
but maybe i am going overboard now.
This way i can use the function in the from clause of the query that
i already had to do to get the totals formatted with to_price. In this
case, i believe it is better to leave out Go’s function because it would
force me to perform two queries.
Instead of binding a nullable string pointer with the payment’s slug,
i wanted to use pgtype’s zeronull.Text type, but it can not work in this
case because it encodes the value as a text, while the parameters is
uuid. I can not use zero.UUID, because it is a [16]byte array, while i
have it in a string.
Thus, had to create my own ZeroNullUUID type that use a string as a base
but encodes it as a UUID.