0071

Customers Who Never Ordered

Filtering
Easy
isempty
setrange
cust-ledger-entry

Customers Who Never Ordered

Marketing is planning a re-engagement campaign and needs the classic anti-join question answered in AL: which customers went a whole period without a single posted transaction? In Business Central every posted sales document and payment leaves a trail in the customer ledger — a customer who "never ordered" in a period is simply a customer with no trace there.

Requirements

Create a codeunit named "Never Ordered Customers" with two public procedures:

procedure NeverOrderedInPeriod(CustomerNo: Code[20]; FromDate: Date; ToDate: Date): Boolean
procedure GetNeverOrderedCustomers(FromDate: Date; ToDate: Date): List of [Code[20]]

Rules:

  1. NeverOrderedInPeriod returns true when the "Cust. Ledger Entry" table holds no entry for CustomerNo whose "Posting Date" falls between FromDate and ToDate, both days inclusive — and false as soon as at least one such entry exists.
  2. Entries posted before FromDate or after ToDate do not make a customer "ordered" — a customer whose entries all fall outside the period has still never ordered in it.
  3. Any ledger entry inside the period counts, whatever its document type, amount or open state — and only entries of that customer count, never a neighbour's.
  4. GetNeverOrderedCustomers returns the "No." of every record in the Customer table for which NeverOrderedInPeriod is true for that period; customers with at least one entry in the period are left out.
  5. Neither procedure may raise an error; a customer with no ledger entries at all has never ordered in any period.

What the tests check

The grading tests create fresh customers, generate a random period per test, and write customer ledger entries directly with posting dates derived from that period: one in the middle, one exactly on FromDate, one exactly on ToDate, one the day before, and one the day after. The boolean is asserted at each of those boundaries, and one test seeds a single in-period entry and asks about both customers in the same period — the entry's owner must come back ordered and the other customer never ordered. The list tests assert membership only — the returned list must contain the seeded never-ordered customers (including one whose entries fall just outside both ends of the period) and must not contain the seeded ordered ones, including customers whose only entry falls exactly on FromDate or exactly on ToDate; the tests run in a real company that already contains customers, so a total count is never asserted.

Learn More

Hint 1
Two filters describe 'ordered in the period': one on "Customer No.", one on "Posting Date". Set both on a "Cust. Ledger Entry" record variable, and the question becomes: is anything left in the filtered set?
Hint 2
SetRange on a date field takes a from-value and a to-value in one call, and the range it keeps is inclusive at both ends — no comparison expressions needed. The list procedure is a plain FindSet loop over Customer that asks your boolean about each record and collects "No." into the list.
Hint 3
IsEmpty() answers 'is the filtered set empty?' with the cheapest query BC can send — it stops at the first hit. Count() = 0 or FindFirst() would give the same answer while fetching more than the question needs; the idiomatic anti-join probe is SetRange, SetRange, IsEmpty.
ALBusiness Central 28.4
Press Compile to check your code compiles — Submit runs the tests.
The code editor is desktop-only
Open this problem on a computer to write and run code. Reading the description, tests and discussion works fine here.