Software offer type "LogShip Cloud" + maintainable catalog in iDempiere
Second offer type next to the 3PL fulfillment quote on the lead page: a SaaS offer for the LogShip Cloud product with its own modal (packages, overage staffel, feature matrix, add-ons, onboarding package with phases/highlights, positions, requirements, texts) and branded PDF (package card, prices, 4–5 column package comparison, onboarding section, example calculation). Header switch "Fulfillment | LogShip Cloud" in both modals; offers/quote preview+send branch on payload.offerType, conditions saved as offer_conditions.softwareQuote.
Catalog persisted in iDempiere (CUST_SoftwarePlan / CUST_SoftwareCatalogItem, enum columns as AD_Reference lists): server util with 5-min cache and fail-soft defaults, routes for read / lists / whole-catalog save / seed / seed-lists, staff gate option requireChat=false. Settings → Software-Katalog is a matrix editor (packages = columns, features = rows, click cell = minimum package, mode pill, drag & drop rows/columns, inline rename, details panel, add-ons list, one save) — linked from Sales in both menus. Offers carry a catalog snapshot so old PDFs stay reproducible.
Also: backdrop close only on a real backdrop click (both offer modals), feature rows no longer squeezed by the global select width, dark-mode styling for the catalog page, the KI console and the offer modals, template clean-up keeps the two new tables, recipe .claude/rules/offers/software-quote.md.
The onboarding package prints as optional with an "ab 3.250 €" starting price (final fixed price after the kickoff) and a note that travel and accommodation costs for on-site appointments are billed as incurred — in the price table, the onboarding section, the example calculation and the Hinweise. Both aspects are per-offer switches in the modal (default on) and persist with the conditions.
LogShip offer: editable fee-line name and subtext, package taglines
The monthly software-fee row prints "<package> — monatliche Softwaregebühr" with the package tagline (catalog Description) and the included volumes as subtext; name and subtext can be overridden per offer (empty = automatic). The package card shows the tagline too. Default catalog names are now Start / Wachstum / Premium / Enterprise with taglines (e.g. Start: "Der komplette Fulfillment-Kern für Ihren Einstieg."); the live rows were updated to match. Modal gets tagline, fee-name and fee-subtext fields (de/en/es).
Returns: count only received customer returns as returned qty, link receipt lines to reused marketplace RMA lines
The mobile + desktop return pages summed every completed M_RMA on the shipment as already returned. Marketplace-imported RMAs (Amazon) are Completed with the expected qty before the parcel arrives, so unprocessed returns showed Remaining 0/1 and could not be entered (e.g. shipment 536925 / RMA 30614). Shared useReturnedQty composable now counts only completed/closed customer-return receipts linked via M_RMA_ID.
quick-rma: when reusing such an RMA, receipt lines are matched to RMA lines by shipment line id (then product, then line number restricted to unlinked lines) instead of the bare line number, which the Amazon importer assigns as a counter. Orphan check also accepts closed receipts.