CluPilotCloud/app/Models
nexxo 9da1358802 Stop charging VAT to the customers who owe us none
A verified EU business outside Austria gets reverse charge — rate 0, no VAT
line, the note on the invoice — and was still charged 214,80 € for a 179,00 €
package, because one Stripe Price carried the domestic gross for everybody.
There is no VAT line on their document to reclaim, so it was a flat 20 %
surcharge on exactly the customers who read their invoices, and the document
then stated the whole 214,80 € as net at 0 %, so they self-accounted their own
VAT on a base a fifth too large.

A Stripe Price can ask who is buying after all: there are two per sellable
thing now, the domestic gross and the bare net, both live on one Product, and
the checkout picks by TaxTreatment. The rule is Austria B2B 20 %, other EU B2B
without VAT — a domestic business still pays the gross, reclaims it as input
tax, and the price on the website is still the price charged for them.

- stripe:sync-catalogue mints and archives both halves of every pair, per plan
  and per module; `reverse_charge` on stripe_plan_prices/stripe_addon_prices
  says which is which, and joining it on subscriptions.stripe_price_id says
  which one a running contract is billed on. A rate change moves the gross
  Price and leaves the net one alone, because the net owes nothing to the rate.
- A status that changes after the sale converges: a verification (or a lapse)
  makes stripe:reprice-subscriptions move the contract onto the other Price
  with PRORATE_NONE, because the term is already paid for. The module items
  follow the same way.
- Downstream follows: the invoice total equals what was taken, the proof
  register expects the figure this customer is actually charged (so a correct
  reverse-charge sale is no longer flagged as a mismatch, and an overcharged
  one is), the setup fee obeys the same rule, and the booking page quotes what
  it will charge.
- StripeClient gained activatePrice(). Archived Prices were brought back in our
  own table and left inactive at Stripe, so a rate that moved and moved back
  pointed the catalogue at a Price no checkout could be opened for.

An unverified number is still charged the gross — an unchecked string must
never be a discount — and where the net Price is missing the checkout refuses
rather than reaching for the domestic one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 02:15:41 +02:00
..
Builders feat(billing): a proof register, and modules frozen at their booked price 2026-07-26 13:07:34 +02:00
Concerns feat(engine): core provisioning data model + hosts 2026-07-25 09:49:43 +02:00
Backup.php feat(engine-b): customer domain models + migrations 2026-07-25 11:38:01 +02:00
Customer.php Ask whether they are a consumer, and let one change their mind 2026-07-29 21:06:06 +02:00
Datacenter.php Let a datacenter code be corrected while nothing depends on it, and say which building 2026-07-28 22:25:21 +02:00
DnsRecord.php feat(engine-b): customer domain models + migrations 2026-07-25 11:38:01 +02:00
ExportTarget.php Let a destination say how it is laid out and how long anything stays 2026-07-29 03:08:27 +02:00
Host.php Make host onboarding survive the machine it runs on 2026-07-30 00:54:16 +02:00
InboundMail.php Read the support mailbox into the console 2026-07-29 21:41:29 +02:00
Incident.php Rebuild the status page as a status page 2026-07-29 12:45:18 +02:00
IncidentUpdate.php Rebuild the status page as a status page 2026-07-29 12:45:18 +02:00
Instance.php Fix nine defects in the provisioning pipelines 2026-07-30 01:34:55 +02:00
InstanceMetric.php Measure availability, and let a customer move down again 2026-07-27 16:41:15 +02:00
InstanceTraffic.php feat(traffic): meter the monthly allowance, show it, throttle instead of blocking 2026-07-25 23:33:47 +02:00
Invoice.php Invoice every renewal, and tell Stripe when the package changes 2026-07-29 19:35:15 +02:00
InvoiceExport.php Let the export have as many destinations as somebody wants 2026-07-29 02:43:15 +02:00
InvoiceSeries.php Lay the foundation for self-issued invoices: series, numbers, frozen documents 2026-07-29 00:57:06 +02:00
LoginSession.php Recognise the devices an account signs in from, and warn about a new one 2026-07-28 23:28:34 +02:00
MailTemplate.php Show the customer, and write the answers once 2026-07-29 22:37:30 +02:00
Mailbox.php Clear every mailbox's verification when the shared server config changes 2026-07-28 06:36:06 +02:00
MaintenanceNotification.php fix(admin): in-flight claim (claimed_at) for exactly-once maintenance send; scope permission rollback 2026-07-25 16:38:44 +02:00
MaintenanceWindow.php fix(portal): scope the per-instance maintenance badge to that instance's host 2026-07-25 19:12:33 +02:00
MonitoringTarget.php Move the console off /admin, give the status page its own address, and measure monitoring 2026-07-27 06:05:40 +02:00
OnboardingTask.php feat(engine-b): customer domain models + migrations 2026-07-25 11:38:01 +02:00
Operator.php Give the people who run CluPilot a table of their own 2026-07-28 09:20:54 +02:00
Order.php Stop charging VAT to the customers who owe us none 2026-07-30 02:15:41 +02:00
PlanFamily.php Let the owner mark one plan as recommended, from the console 2026-07-29 14:22:22 +02:00
PlanPrice.php feat(billing): the plan catalogue becomes three tables, and config stops selling 2026-07-26 12:05:56 +02:00
PlanVersion.php feat(billing): the plan catalogue becomes three tables, and config stops selling 2026-07-26 12:05:56 +02:00
ProvisioningRun.php Fix nine defects in the provisioning pipelines 2026-07-30 01:34:55 +02:00
ProvisioningStepEvent.php feat(engine): core provisioning data model + hosts 2026-07-25 09:49:43 +02:00
RunResource.php feat(engine): core provisioning data model + hosts 2026-07-25 09:49:43 +02:00
Seat.php feat(portal): settings page — company profile, branding (logo+colors), cancel package, close account 2026-07-25 14:36:52 +02:00
SentMail.php Keep a register of what was sent, and answer the customer from here 2026-07-29 21:02:36 +02:00
StatusDay.php Rebuild the status page as a status page 2026-07-29 12:45:18 +02:00
StripeAddonPrice.php Stop charging VAT to the customers who owe us none 2026-07-30 02:15:41 +02:00
StripePendingEvent.php feat(billing): Stripe owns the billing cycle, we own capability 2026-07-26 13:36:28 +02:00
StripePlanPrice.php Stop charging VAT to the customers who owe us none 2026-07-30 02:15:41 +02:00
Subscription.php Say the same thing to the customer, the register and the bank 2026-07-30 01:30:24 +02:00
SubscriptionAddon.php Bill a booked module every month, and put it on the invoice 2026-07-29 20:22:02 +02:00
SubscriptionRecord.php Ask whether they are a consumer, and let one change their mind 2026-07-29 21:06:06 +02:00
SupportRequest.php Editing in modals, an update button that is not gated on a stale reading, and a support page that is real 2026-07-27 17:55:49 +02:00
User.php Give people a way back in, and put the URL in English 2026-07-29 16:50:29 +02:00
UserDevice.php Repair two comment blocks the admin-hosts edit ran together 2026-07-28 23:20:43 +02:00
VpnPeer.php Move the console's identity out of the customer table 2026-07-28 10:31:43 +02:00