Skip to content

Recurring Document Flow

A recurring agreement describes a customer or supplier arrangement that produces documents on a schedule: monthly support fees, quarterly licences, weekly deliveries, a monthly rent or hosting bill from a vendor. The recurring engine materialises one occurrence per period, runs the pre-checks, and generates the real business documents — on the customer side a sales order and delivery note or an AR invoice directly; on the supplier side a purchase order, a draft goods receipt and an AP bill — through the same handlers a person would use. Nothing is booked twice: every period has one business key and the engine checks it before it acts.

Hub: Target Document Flow. Neighbours: Sales, Accounts Receivable, Purchasing, Accounts Payable, ETA.

Screens: /sales/recurring (operations dashboard; ?direction=Customer|Supplier scopes the view), /sales/recurring/agreements and /sales/recurring/occurrences (customer), /pc/recurring/agreements and /pc/recurring/occurrences (supplier).

#Chain overview

Agreement → occurrence → documents → ledgers

flowchart LR
  RA[Recurring Agreement] --> OC[Occurrence per period]
  OC -->|Customer · InvoiceOnly| AR[AR Invoice]
  OC -->|Customer · OrderAndDelivery| SO[Sales Order]
  SO --> DN[Delivery Note]
  DN --> AR
  AR --> GL[GL Journal]
  AR --> ETA[ETA e-invoice]
  OC -->|Supplier · InvoiceOnly| AP[AP Bill]
  OC -->|Supplier · PurchaseOrderAndReceipt| PO[Purchase Order]
  PO --> GRN[Goods Receipt · draft]
  PO --> AP
  GRN -.->|posted by operator| GL
  AP -->|approved| GL

  class RA egkDraft
  class OC,SO,DN,PO,GRN egkActive
  class AR,AP,GL egkSuccess
  class ETA egkWarn
Recurring agreement (RecurringAgreementStatus)

#Statuses

Draft, Active, Suspended, Cancelled, Expired

Agreement lifecycle

flowchart TB
  Start((Create)) --> Draft[Draft]
  Draft -->|Activate| Active[Active]
  Active -->|Suspend| Suspended[Suspended]
  Suspended -->|Resume| Active
  Active -->|Cancel| Cancelled[Cancelled]
  Active -->|End date reached| Expired[Expired]
  Active -->|Auto-renew| Active

  class Start egkStart
  class Draft egkDraft
  class Active egkSuccess
  class Suspended egkWarn
  class Cancelled egkError
  class Expired egkTerminal

#Header fields that drive generation

Field Effect
Direction Customer (AR side: sales order / delivery / AR invoice) or Supplier (AP side: purchase order / goods receipt / AP bill)
Fulfillment mode Customer: InvoiceOnly → AR invoice per period; OrderAndDelivery → sales order → delivery → AR invoice. Supplier: InvoiceOnly → AP bill per period; PurchaseOrderAndReceipt → purchase order → draft goods receipt → AP bill
Recurrence rule Frequency (weekly, monthly, quarterly, yearly, custom days) and anchor day
Billing timing InAdvance (run date = period start) or InArrears (run date = period end)
Start / end date, auto-renew, renewal term Term of the agreement; the scheduler never creates a period after the end date
Grace days How late an automatic run may still generate the period; later periods expire (a manual run ignores the window)
Company, branch, partner branch Stamped on every generated document (ledger and ETA issuer)
Warehouse, price list, payment terms Defaults for order-and-delivery agreements; on the supplier side the warehouse decides whether a purchase order and goods receipt are raised (stock item) or only the bill (service)
Auto post Customer: post the AR invoice immediately (GL + ETA) or leave it as a draft for review. Supplier: submit the AP bill into the approval workflow and record the agreement owner as the purchase-order approver; never posts the goods receipt

An agreement needs at least one line to activate. Lines carry description, quantity, unit price, optional catalog product, discount, and ETA-aware tax rows (fractions, e.g. 0.14).

#Actions

Action Availability Behavior
Activate Draft, Suspended Aligns the scheduling cursor to the anchor day and starts materialising periods
Suspend Active Open periods stay Scheduled but are parked as PendingReview when due
Resume Suspended Continues from the cursor; no back-generation of skipped periods without a manual run
Cancel any except terminal Open periods are Expired at their next run
Schedule now Active Materialises periods up to the horizon (default 30 days)
Occurrence (RecurringOccurrenceStatus)

#Statuses

Scheduled, Due, Processing, Generated, Submitted, Completed, Failed, PendingReview, Cancelled, Expired

Occurrence lifecycle — lease, pre-checks, generate

flowchart TB
  Sch[Scheduled] -->|Run date reached| Due[Due]
  Due -->|Lease claimed| Proc[Processing]
  Proc -->|Pre-check blocks| Failed[Failed]
  Proc -->|Needs a person| Review[PendingReview]
  Proc -->|Documents created| Gen[Generated]
  Gen -->|ETA accepted or no bridge| Done[Completed]
  Gen -->|ETA submitted| Sub[Submitted]
  Sub --> Done
  Failed -->|Retry window| Due
  Review -->|Run manually| Proc
  Due -->|Grace elapsed| Exp[Expired]
  Sch -->|Agreement cancelled| Can[Cancelled]

  class Sch egkDraft
  class Due,Proc,Gen,Sub egkActive
  class Done egkSuccess
  class Review egkWarn
  class Failed egkError
  class Exp,Can egkTerminal

#Business key and idempotency

Every occurrence carries IdempotencyKey = REC:{agreementId}:{periodStart:yyyyMMdd}. The key is unique in the database and is handed to the AR invoice factory or the AP bill factory (ExternalIdempotencyKey) or stamped as the sales-order Reference; supplier purchase orders use the deterministic number RPO-{agreement}-{yyyyMMdd}. A replayed job, a second worker, or a double click therefore finds the existing documents and links them instead of generating again. An occurrence that already has an AR or AP invoice is closed as already generated without touching the ledgers.

#Pre-checks (before any document is created)

Code Outcome Meaning
CompanyMissing Failed No company on the agreement and the tenant has zero or several active companies
AgreementInactive, PeriodOutsideTerm, InvalidPeriod, NoLines Failed Structural problems; retrying will not help
InvalidTaxRate, InvalidCurrency, InvalidExchangeRate Failed Tax rates must be fractions, currency a 3-letter ISO code
PartnerMissing, PartnerInactive, PartnerRoleMissing, PartnerBranchMismatch Failed Partner state
CreditHold, ZeroPrice, ProductInactive, MaterialMissing, StockCheckFailed PendingReview A person decides
InsufficientStock PendingReview Order-and-delivery only: ATP in the agreement warehouse is below the period quantity
WarehouseMissing, WarehouseInactive, ProductMissing Failed Order-and-delivery master data

A credit-limit refusal raised by the AR factory during generation is also parked as PendingReview.

#What gets generated

Mode Steps (each idempotent) Documents linked on the occurrence
InvoiceOnly AR invoice via the AR invoice factory with the occurrence key; posted when Auto post is on ArInvoiceId
OrderAndDelivery Sales order (Reference = key) → Confirm → Delivery note → Ship → AR invoice from delivery (DLV:{deliveryId}) SalesOrderId, DeliveryNoteId, ArInvoiceId
Supplier InvoiceOnly AP bill via the AP bill factory with the occurrence key; submitted for approval when Auto post is on, otherwise Draft ApInvoiceId
Supplier PurchaseOrderAndReceipt Purchase order RPO-{agreement}-{yyyyMMdd} (Submitted; Approved when Auto post is on) → draft goods receipt against it → AP bill linked to both. Without a warehouse (service) only the bill is raised PurchaseOrderId, GoodsReceiptId, ApInvoiceId

The generated AR invoice then follows the normal cycle: ArInvoiceSentEvent → GL journal (Dr AR control / Cr revenue / Cr tax), ArInvoicePostedIntegrationEvent → ETA e-invoice draft when the company has an enabled ETA bridge setting. ETA status events (Valid, Cancelled, Rejected) close or flag the occurrence through the AR ↔ ETA sync.

On the supplier side the engine never books stock or a liability by itself: the goods receipt stays Draft until Purchasing posts it on physical receipt (that posting moves stock and raises the GRN journal), and the AP bill goes through the normal AP approval before its journal (Dr expense or GR/IR / Cr AP control) is posted. The occurrence is Completed once the three documents exist; the open receipt shows in the Purchasing worklists, not in the recurring review queue.

#Actions

Action Availability Behavior
Run now Scheduled, Due, Failed, PendingReview Manual execution; ignores the grace window and takes over an expired lease
Review → resolve PendingReview Fix data (stock, credit, prices) then Run now
Cancel period Scheduled, Due, PendingReview, Failed Period will never be generated
View documents Generated and later Opens the sales order / delivery / AR invoice / e-invoice, or the purchase order / goods receipt / AP bill
Engine, jobs and safety
  • Scheduler job (ScheduleRecurringOccurrencesCommand): materialises periods up to ScheduleHorizonDays for every active agreement in every tenant.
  • Processor job (ProcessDueRecurringOccurrencesCommand): claims due occurrences with a database lease (ExecuteUpdate, LeaseMinutes, worker id), executes them one by one, writes a RecurringExecutionLog row per attempt and releases the lease. A crashed worker's lease expires and another worker takes over — never generating twice because the business key is checked first.
  • Retry: Failed (retryable) occurrences are picked up again after the retry delay; Failed (permanent) and Expired are not.
  • Notifications: failures, insufficient stock, expired periods and ETA refusals raise operator notifications; the dashboard at /sales/recurring shows due / processing / review / failed counters, next runs and the last execution log per agreement.
  • Multi-tenant: jobs fan out per tenant (TenantFanOutJob); every command runs inside the tenant's scope and company.
  • Legacy AR recurring templates were migrated into agreements (MigrateArRecurringTemplates); the old template screen remains read-only.

#Post-action sync

The agreement and occurrence lists reload after every action and show the engine's status, last message and linked documents. A failed run leaves the previous status, records the attempt in the execution log and (for retryable failures) schedules the next attempt.

#Scenarios

Scenario Outcome
Monthly support, invoice only, ETA bridge enabled AR invoice posted, GL journal, ETA draft; occurrence Generated until ETA reports Valid → Completed
Same period processed twice (job replay) Second run reports documents already generated; no new invoice, journal or e-invoice
Order-and-delivery with stock below quantity PendingReview with InsufficientStock; after stock arrives, Run now generates order → delivery → invoice once
Customer on credit hold PendingReview with CreditHold; nothing invoiced
Automatic run later than grace days Expired; a manual run can still generate the period
Agreement cancelled with open periods Open periods Expired at their next run
Monthly hosting bill from a vendor (supplier, invoice only) AP bill keyed by the period; approval and the AP journal follow the normal AP flow
Weekly stock delivery from a vendor (supplier, PO and receipt, warehouse set) Purchase order + draft goods receipt + linked AP bill in one run; the storekeeper posts the receipt when the goods arrive — only then does stock move
Worker crashes after the purchase order was created The retry finds RPO-… for the period, reuses it, and continues with the receipt and the bill — no second PO

Reconnecting to the server…

This page stays right here while we reconnect.

Still waiting? You can reload the page.