findOne and findOneByUuid mapped order.cart before checking that the order
exists, and the service's findOneByUuid did the same, so a missing order
threw a TypeError: GET /orders/:uuid/cart, add-product, remove-product,
generate-invoice and generate-delivery-note answered 500.
The service's findOneByUuid now returns null for a missing order, and each
caller handles it: the controllers answer 404, sendInvoice and
sendDeliveryNote throw a NotFoundError.
Refs libretech/mp#71
parseCustomerUpdate accepts { data } with a subset of the seven fields the
shop's checkout sends: email, acceptedTermsAndConditionsAt, invoiceAddress,
deliveryAddress, invoiceAddressStructured, deliveryAddressStructured and
delivery. Any other key, at the top level, in data or in a structured
address, rejects the whole body, and the errors name the fields without
repeating their values.
The terms are recorded at the server's time whenever the client sends a
true value: the invoice is dated by it.
Refs libretech/mp#71
- src/checkout/ (PayPal order request, pricing, VAT) and src/logging/format.ts
hold the checkout's pure code, moved out of PayPalApi, the product service
and config without a behaviour change.
- npm test runs node:test on the TypeScript sources (type stripping), with no
dependencies: 21 tests, 13 pin today's output, 8 describe the correct
behaviour for the known defects of libretech/mp#67
and are marked todo.
- CI: the tests run in the image's base (node:22.14.0-alpine) for every pull
request, push to main and release tag. The image is built for pull requests
and built and published only for v* tags; main no longer publishes :main
and :sha-* images.
Refs libretech/mp#71
Co-authored-by: Michael Czechowski <mail@librete.ch>
Recovered work in progress from 2026-05. A product-cover can be marked
standalone, and a standalone product (Journaling Booklet) prices as the
cover price without the pattern/pages/ruling lookups, which do not exist
for it. The remaining component lookups are now guarded so a missing
relation yields null instead of calling findOne with undefined.
Source moved verbatim from mp/cms/ on 2026-04-29; mp was the first
concrete adapter consuming the libreshop toolkit. Builds and publishes
git.librete.ch/libreshop/cms on every main / v* push via the standard
.gitea/workflows/build.yml shared across libreshop components.