Credit memos

TL;DR

The money going back to the customer. Its amounts are negative, it carries the cost of making the refund, and it records whether the refund actually succeeded.

A credit memo is the refund document. It arrives from NetSuite and points back at the return authorization that permitted it, the invoice it credits, and the order underneath both.

Where it comes from

app/jobs/netsuite/creditmemos.php consumes the ns_creditmemos batch type, one batch per date. It is not part of the verkoopdump backfill, so pre-2022 refunds are not in CCC.

classify/creditmemos.php calls getblocked(array('ns_salesorders', 'ns_invoices', 'ns_returnauthorizations')) and ends the run while any of the three is still queued, because the credit memo points back at all of them.

Tables

TableHolds
creditmemosThe header
creditmemolines, creditmemolinesnoninvItem and non-inventory lines
creditmemodiscount, creditmemotaxDiscounts and taxes
creditmemoclassificationsHeader flags, including refundfailed
creditmemolineclassificationsLine flags

A failed refund is recorded, not hidden

refundfailed is set when a refund was attempted and did not succeed: the credit memo carries a refund date that is a real date, and the upstream success flag is not set. A credit memo with refundfailed = 1 exists, credits the order, and the money did not reach the customer. Filter on it on /transactions/creditmemos when reconciling refunds against payments.

Its amounts are already negative

amount_eur_invat on a credit memo is negative, because the money moves the other way. This is what makes the refund payment-cost rule's percentage negative on purpose: the impact is field_value * percentage / 100, so a positive percentage against a negative amount would credit the margin instead of costing it. See dates, currency and signs.

What it contributes to margin

The cost of paying money back, seeded from config/seed/cm_calculationrules/payment_refund.csv. That file pairs with payment.csv, which is the same shape on the prepayment, and today it is one row: a single rate on every credit memo, with the method and currency columns left empty so the rule filters on neither. An empty column there means exactly that, and the rule name drops that part with it.

calculate/creditmemos.php rebuilds a day at a time, deleting that date's credit memo impacts from all four contribution groups before recalculating, and re-queues every order it touched into cm_impact_salesorders.

Where to look, and where to change

WhatWhere
The credit memos themselves/transactions/creditmemos
One credit memo in the context of its order/transactions/salesorderdetail, under related records
The refund cost rule/contributionmargin/calculationrules

The credit memo is upstream data and is not editable in CCC. The rule that prices it is.

Evidence

Import and blocking from app/jobs/netsuite/creditmemos.php and app/jobs/classify/creditmemos.php. refundfailed from classify->classifycreditmemo(). Table family from app/jobs/install/install.php. Day rebuild from app/jobs/calculate/creditmemos.php. The negative percentage and its reason from AGENTS.md, Configuration and .env, and config/seed/cm_calculationrules/payment_refund.csv. Columns from templates/main/transactions/creditmemos.tpl. Basis: code-checked at 30f5313.