The BC Integration Worker (BCIntegration.Worker) is a .NET 9 hosted Windows Service. It runs on a 5-minute loop and does three kinds of work:
Inbound from BC — payment application data and ledger entries (read-only API calls into BC, writes into SMART SQL).
Outbound to BC — drains TransmissionMaster rows with Status = 'N' and posts custom API entities in Business Central.
Status bookkeeping — sets TransmissionMaster.Status to C (completed) or E (error) with ReturnMessage / ReturnCode.
SMART desktop and web apps enqueue work by inserting into TransmissionMaster and the child Transmission* tables below. This worker is the component that actually talks to BC.
For the business picture of transmissions across all SMART apps, see How SMART talks to Business Central. This page is the technical reference for what this worker implements today (source: Worker.cs, Api/ApiService.cs on main).
15TransmissionMaster.TransType codes are processed (lot, vend, … cpcl, soap).
14 child Transmission* tables are read (plus TransmissionPaymentStatus on inbound pays).
TransmissionPOGenerate and TransmissionLandDevBudgetActuals exist in the Core project but are not used by the worker.
Ledger mirror tables BCJobLedger and BCGeneralLedger are filled from BC; they are not transmission queues.
OAuth2 client-credentials against login.microsoftonline.com; company-scoped OData under ApiSettings:BaseUrl.
When Sync:BCLedgersOnly is false (production default), each cycle runs in order:
Step
What happens
1
Payment status pull (pays) — For each calendar day not yet marked, GET BC projectAppliedEntries for yesterday (relative to Eastern time). Insert TransmissionMaster (TransType = pays, Status = N or C) and child TransmissionPaymentStatus rows when BC returned data.
2
BC job ledger sync — GET /jobLedgers in 5,000-entry windows; insert into BCJobLedger where PostingDate > 2025-12-31.
3
BC general ledger sync — GET /glEntries for entry numbers that exist on BCJobLedger but not yet on BCGeneralLedger (also after cutoff date).
4
Transmission drain — SELECT * FROM TransmissionMaster WHERE Status = 'N' AND TransType IN (...) — process each row (see catalog below).
Remove entity (supported in API layer; uncommon in queue).
For comm, lot, vend, vetp, and vadr, the worker may flip A → U before posting: it GETs the BC record first (ResolveAddOrUpdateAsync). Batch types esti and acgr do the same GET-per-row inside ProcessBatchWithTransmissionAsync.
Only these SQL tables participate in the worker loop. Other Transmission* tables in SmithDouglasCommunities may be filled by SMART for other processes; they are out of scope for this service unless added to Worker.cs.
Child table
TransType
Direction
BC API (custom publisher)
TransmissionMaster
(all)
Control
—
TransmissionCommunity
comm
SMART → BC
/dimensionValues (COMMUNITY)
TransmissionLot
lot
SMART → BC
/dimensionValues (LOT), then /projects, /defaultDimensions on add
TransmissionDivision
divi
SMART → BC
/dimensionValues (DIVISION)
TransmissionVendor
vend
SMART → BC
/vendors
TransmissionVendorType
vetp
SMART → BC
/vendorPostingGroups
TransmissionVendorAddress
vadr
SMART → BC
/vendorRemitAddresses
TransmissionTerm
term
SMART → BC
/paymentTerms
TransmissionPOApprove
poap, soap
SMART → BC
/unpostedPurchaseDocs + /unpostedPurchaseDocLines
TransmissionContractPostClosing
cpcl
SMART → BC
/postClosingJobGLJrnlLines (one POST per amount column)
TransmissionInvoiceSubmission
inv1
SMART → BC
/unpostedPurchaseDocs + /unpostedPurchaseDocLines
TransmissionEstimate
esti
SMART → BC
/projects, /projectTasks
TransmissionLotActivityGroup
acgr
SMART → BC
/projects, /projectTasks
TransmissionLotStatus
lsts
SMART → BC
OData /$batch PUT on LOT dimensionValues
TransmissionPaymentStatus
pays
BC → SMART
GET /projectAppliedEntries (inbound); no BC POST on drain
Not created by SMART users directly. The worker creates one TransmissionMaster per calendar day being reconciled (Eastern time), starting the day after the last paysDateAdded.
TransmissionMaster — CompanyID = SGW, TransType = pays, Mode = A. Status = C if BC returned no rows (day marked done); Status = N if there are rows to process.
TransmissionPaymentStatus — bulk insert when data exists.
On drain (case pays): PermanentOrderLog updated — CheckDate = PostingDate, CheckNumber = AppliedDocNo where PONumber = DocumentNo (rows with valid integer PO and non-null AppliedDocNo only).
TransmissionMaster set to C — no outbound BC API call.
POST/PUT /dimensionValues: dimensionCode = LOT, code = formatted lot (CCC-BBB-UUU from 12-char LotAccountCode), name = ProjectDesc
On A only (extra calls)
POST /projects if missing (no, sellToCustomerNo, dimensions). PUT /defaultDimensions for LOT, COMMUNITY, DIVISION on table 167 (skipped for placeholder lots ending in XXXXXXX).
PerformPostAsync / PerformPutAsync implement TransmissionDivision, but ProcessTransmission<T>’s type guard does not list TransmissionDivision — verify in your build that divi rows are not logging “Unsupported type” before relying on this path.
TransmissionContractPostClosing (single row; joined to ContractPostClosing, Contract, Community, Company, user name)
Modes
Uses master Mode on each line POST
Sent to BC
One POST per non-zero amount field to /postClosingJobGLJrnlLines: documentNo = ContractSysID, postingDate = ClosedDate, accountNo (G/L or bank), amount (sign rules per column), jobNo / jobTaskNo / dimensions vary by column (e.g. CashToSeller clears project; WorkInProgress uses task JOURN; land dev columns use D1015 / CLOTC)
Amount columns
PlanPrice, OptionRevenue, LotPremium, PriceAdj, CashToSeller, WIPLandDevDebit/Credit, LandDevelopmentLotCost, ConstructionCost, DepositReceived, closing costs, warranty, concessions, etc. (see AccountToColumns in ApiService.cs)
Key SMART columns
ContractSysID, ClosedDate, ClosingDesc, PostClosingBankAcct, CommunityID, financial columns on ContractPostClosing
GET /projectTasks; if missing → A, else U. AddProject first. POST/PUT /projectTasks: projectNo, projectTaskNo, description = TaskDesc, originalBudget = BudgetAmount
Authentication: OAuth2 client credentials (grant_type from config). All company API calls use BaseUrl; OData batch (lsts) uses RootUrl + companies({CompanyGUID}).
Logging: Serilog → console and C:\Logs\BCIntegration.log (daily rolling).